/*
 * Copyright 2011 Drew Wilder-Goodwin, Steve Feng
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#include "resources.h"
#include "cmdline.h"
#include "dxdiagcom.h"
#include "globals.h"
#include "utility.h"
#include "wine/unicode.h"
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <winuser.h>
#include <wchar.h>

#define MAX_OUTPUT_STRING_LEN 8192

static BOOL PrepareFilename(WCHAR* filename, const enum OUTFORMAT format);
static void OutputPlainFile(FILE * hFile);
static void OutputXMLFile(FILE * hFile);
static void OutputSectionHeading(int section, FILE * hFile);
static BOOL OutputWString(FILE * hFile, WCHAR * buffer);

/*
 Process options [/WHQL:ON|OFF][/X outfile][/T outfile]
 Returns TRUE if options were present, FALSE otherwise
 FIXME: Native behavior seems to be:
 Only one of /X and /T is allowed, /WHQL must come before /X and /T,
 quotes are optional around the filename, even if it contains spaces.
 If a filename is given without an option flag, /t is assumed.
 */

BOOL ProcessCommandLine(const WCHAR * s)
{
    WCHAR outfile[MAX_PATH + 1];
    int opt_t = FALSE;
    int opt_x = FALSE;
    int opt_help = FALSE;
    int opt_given = FALSE;
    LPWSTR *wargv;
    int nArgs;
    int i;

    outfile[0] = 0;

    /* FIXME: this won't work if the filename has spaces */
    wargv = CommandLineToArgvW(s, &nArgs);

    for (i = 1; i < nArgs; i++) {
        const WCHAR *s = wargv[i];
        if (*s != '-' && *s != '/') {
            /* No flag -> same as /t (or maybe it is a /t or /x argument) */
            lstrcpyW(outfile, s);
            if (!opt_x)
                opt_t = TRUE;
            opt_given = TRUE;
            continue;
        }
        s++;
        switch (*s++) {
        case 'T':
        case 't':
            opt_t = TRUE;
            opt_given = TRUE;
            break;
        case 'X':
        case 'x':
            opt_x = TRUE;
            opt_given = TRUE;
            break;
        case 'W':
        case 'w':
            /* Just ignore the /whql option for now */
            opt_given = TRUE;
            break;
        default:
            opt_help = TRUE;
            opt_given = TRUE;
            break;
        }
    }
    if (opt_help) {
        WCHAR text[MAX_STRING_LEN];
        WCHAR title[MAX_STRING_LEN];
        LoadStringW(g_hinstance, STRING_USAGE, text, MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_WINDOW_TITLE, title,
                    MAX_STRING_LEN);
        MessageBoxW(NULL, text, title, MB_ICONEXCLAMATION | MB_OK);
    }
    if (opt_x)
        OutputToFile(outfile, XML);
    else if (opt_t)
        OutputToFile(outfile, PLAIN);
    return opt_given;
}


/*
 Prepare a given filename for output:
  1. Set to default if empty
  2. Remove any trailing periods
  3. Reject any filenames with invalid characters
  4. Add an extension if not present
 Precondition: filename is a null terminated string with at least enough
               room to store itself and one 4 extra characters (extension)
 Postcondition: the above rules have been applied to filename if valid
 Return: TRUE if the conditions were applied successfully
         FALSE if the conditions could not be applied because filename invalid
 */

static BOOL PrepareFilename(WCHAR* filename, const enum OUTFORMAT format)
{
    static WCHAR s_default_filename[] = {'d','x','d','i','a','g',0};
    static WCHAR s_nonallowed_chars[] = {'<','>',':','"','/','\\','|','?','*',0};
    static WCHAR s_ext_plain[] = {'.','t','x','t',0};
    static WCHAR s_ext_xml[] = {'.','x','m','l',0};
    DWORD filename_len;
    WCHAR *checkchar, *badchar;

    filename_len = strlenW(filename);

    if (filename_len == 0) {
        lstrcpyW(filename, s_default_filename);
        filename_len = strlenW(filename);
    }

    /* trailing periods are not allowed in windows filenames */
    while (filename[filename_len-1] == '.') {
        filename[filename_len-1] = 0;
        filename_len -= 1;
        if (filename_len == 0) {
            /* input string must have been all periods */
            return FALSE; 
        }
    }

    /* some characters not allowed in windows filenames */
    for (checkchar = filename; *checkchar != 0; checkchar++) {
        /* would like to use wcschr() here, but wine's headers use wchar_t
            there so it cannot easily be used */
        for (badchar = s_nonallowed_chars; *badchar != 0; badchar++) {
            if (*checkchar == *badchar) {
                return FALSE;
            }
        }
    }

    /* CHECKPOINT: based on manipulation above:
     *   1. there is at least one non-period character
     *   2. there are no disallowed characters
     *   3. the filename does not end in a period
    */

    /* If there is an extension (i.e. at least one period), were done. */
    for (checkchar = filename; *checkchar != 0; checkchar++) {
        if (*checkchar == '.') {
            /* assumes all other validty checks already done */
            return TRUE;
        }
    }
    
    /* no period was found, extension will be appended */
    /* FIXME: handle size of filename buffer better */
    if (format == PLAIN) {
        lstrcatW(filename, s_ext_plain);
    } else if (format == XML) {
        lstrcatW(filename, s_ext_xml);
    } else {
        /* unknown format */
        return FALSE;
    }

    return TRUE;
}


/*
 Output dxdiag diagnostic information to outfile
 two output formats are supported:
 PLAIN (-t, /t, etc.) and
 XML (-x, /x, etc.)
 Returns FALSE if filename invalid or unable to be opened for writing
 */

BOOL OutputToFile(WCHAR * filename, const enum OUTFORMAT format)
{
    FILE *hFile;
    CHAR utf8Filename[MAX_STRING_LEN];
    char utf_openparams[] = "wb+";

    if (PrepareFilename(filename, format) == FALSE) {
        return FALSE;
    }

    
    WideCharToMultiByte(CP_UTF8, 0, filename, -1, utf8Filename,
                        MAX_STRING_LEN, NULL, NULL);

    if ((hFile = fopen(utf8Filename, utf_openparams)) == NULL) {
        WCHAR messageText[MAX_STRING_LEN];
        WCHAR messageTitle[MAX_STRING_LEN];
        LoadStringW(g_hinstance, STRING_ERROR_OPENING_FILE, messageText,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_ERROR, messageTitle,
                    MAX_STRING_LEN);
        MessageBoxW(NULL, messageText, messageTitle, MB_ICONEXCLAMATION
                    | MB_OK);
        return FALSE;
    }

    if (format == PLAIN) {
        OutputPlainFile(hFile);
    } else if (format == XML) {
        OutputXMLFile(hFile);
    }

    fclose(hFile);

    return TRUE;
}

/*
 Output Helper Functions
 Contains functions that output a variety of formats to the given file.
 */
static BOOL OutputHexString(FILE * hFile, unsigned int buffer)
{
    int nNumberOfBytesWritten;
    nNumberOfBytesWritten = fprintf(hFile, "0x%X", buffer);
    return (nNumberOfBytesWritten >= 0);
}

static BOOL OutputChar(FILE * hFile, CHAR output)
{
    return ((fputc(output, hFile)) == output);
}

static void OutputNewline(FILE * hFile)
{
    OutputChar(hFile, '\r');
    OutputChar(hFile, '\n');
}

static BOOL OutputWString(FILE * hFile, WCHAR * buffer)
{
    DWORD nNumBytesToWrite, nNumBytesWritten = 0;
    DWORD utf8BufferSize;
    CHAR *utf8Buffer;

    if ((!buffer) || (*buffer == '\0'))
        return TRUE;

    utf8BufferSize = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
    utf8Buffer = HeapAlloc(GetProcessHeap(), 0, utf8BufferSize);
    if (utf8Buffer == NULL)
        return FALSE;

    nNumBytesToWrite = WideCharToMultiByte(CP_UTF8, 0, buffer, -1,
                                                utf8Buffer, utf8BufferSize, NULL, NULL);

    if (nNumBytesToWrite != 0)
        nNumBytesWritten = fprintf(hFile, "%s", utf8Buffer);

    HeapFree(GetProcessHeap(), 0, utf8Buffer);

    return nNumBytesToWrite == nNumBytesWritten;
}

/*
 Output a section heading to the file pointed to by hFile in a format:

 ------------
 Section Name
 ------------

 Precondition:  existing string table entry for int section
 hFile is a valid file handle to a writable file
 */

static void OutputSectionHeading(int section, FILE * hFile)
{
    WCHAR text[MAX_STRING_LEN];
    WCHAR separator[MAX_STRING_LEN];
    DWORD len, i;

    LoadStringW(g_hinstance, section, text, MAX_STRING_LEN);

    i = 0;
    len = lstrlenW(text);
    while (len-- > 0)
        separator[i++] = '-';
    separator[i] = '\0';

    OutputNewline(hFile);
    OutputWString(hFile, separator);
    OutputNewline(hFile);
    OutputWString(hFile, text);
    OutputNewline(hFile);
    OutputWString(hFile, separator);
    OutputNewline(hFile);
}

static void OutputNodeBegin(int property, FILE * hFile)
{
    WCHAR text[MAX_STRING_LEN + 3];
    DWORD i;

    text[0] = '<';
    text[1] = '\0';

    LoadStringW(g_hinstance, property, text + 1, MAX_STRING_LEN);
    i = lstrlenW(text);

    text[i++] = '>';
    text[i] = '\0';

    OutputWString(hFile, text);
}

static void OutputNodeEnd(int property, FILE * hFile)
{
    WCHAR text[MAX_STRING_LEN + 4];
    DWORD i;

    text[0] = '<';
    text[1] = '/';

    LoadStringW(g_hinstance, property, text + 2, MAX_STRING_LEN);
    i = lstrlenW(text);

    text[i++] = '>';
    text[i] = '\0';

    OutputWString(hFile, text);
}

static void OutputPlainPropertyTitle(int property, FILE * hFile)
{
    static WCHAR s_colonSpace[] = { ':', ' ', 0 };
    WCHAR text[MAX_STRING_LEN];

    LoadStringW(g_hinstance, property, text, MAX_STRING_LEN);

    OutputWString(hFile, text);
    OutputWString(hFile, s_colonSpace);
}

static void OutputPlainStringTitle(WCHAR * title, FILE * hFile)
{
    static WCHAR s_colonSpace[] = { ':', ' ', 0 };

    OutputWString(hFile, title);
    OutputWString(hFile, s_colonSpace);
}

static void OutputPlainPropertyEqualsTitle(int property, FILE * hFile)
{
    WCHAR text[MAX_STRING_LEN];

    LoadStringW(g_hinstance, property, text, MAX_STRING_LEN);

    OutputWString(hFile, text);
    OutputChar(hFile, '=');
}

/*
 Output a property value pair in the PLAIN format, which looks like:

 <property name>: <property value>

 For example:

 Machine name: drew-mbp
 */
static void OutputPlainProperty(int property, WCHAR * value, FILE * hFile)
{
    OutputPlainPropertyTitle(property, hFile);
    OutputWString(hFile, value);
    OutputNewline(hFile);
}

static void OutputInParentheses(WCHAR * value, FILE * hFile)
{
    OutputChar(hFile, '(');
    OutputWString(hFile, value);
    OutputChar(hFile, ')');
}

static void OutputPlainDebugLevelProperty(int property, WCHAR * current,
                                          WCHAR * max, WCHAR * runtime,
                                          FILE * hFile)
{
    OutputPlainPropertyTitle(property, hFile);
    OutputChar(hFile, '(');
    OutputWString(hFile, current);
    OutputChar(hFile, '/');
    OutputWString(hFile, max);
    OutputChar(hFile, ')');
    OutputChar(hFile, ' ');
    OutputInParentheses(runtime, hFile);
    OutputNewline(hFile);
}

static void OutputPlainDriverVersionProperty(int property, WCHAR * version,
                                             WCHAR * language,
                                             FILE * hFile)
{
    OutputPlainPropertyTitle(property, hFile);
    OutputWString(hFile, version);
    OutputChar(hFile, ' ');
    OutputInParentheses(language, hFile);
    OutputNewline(hFile);
}

static void OutputPlainDirectPlayAdapterProperty(WCHAR * property, WCHAR * value, FILE * hFile)
{
    OutputPlainStringTitle(property, hFile);
    OutputWString(hFile, value);
    OutputNewline(hFile);
}

static void OutputPlainCSP(int property, WCHAR * value1, WCHAR * value2,
                           FILE * hFile)
{
    static WCHAR s_commaSpace[] = { ',', ' ', 0 };
    OutputPlainPropertyTitle(property, hFile);
    OutputWString(hFile, value1);
    OutputWString(hFile, s_commaSpace);
    OutputWString(hFile, value2);
    OutputNewline(hFile);
}

static void OutputPlainSSP(int property, WCHAR * value1, WCHAR * value2,
                           WCHAR * value3, FILE * hFile)
{
    static WCHAR s_space[] = { ' ', 0 };
    OutputPlainPropertyTitle(property, hFile);
    OutputWString(hFile, value1);
    OutputWString(hFile, s_space);
    OutputWString(hFile, value2);
    OutputWString(hFile, s_space);
    OutputWString(hFile, value3);
    OutputNewline(hFile);
}

static void OutputPlainHexProperty(int property, WCHAR * value, FILE * hFile)
{
    OutputPlainPropertyTitle(property, hFile);
    OutputHexString(hFile, (unsigned int) lstrtolW(value, NULL, 10));
    OutputNewline(hFile);
}

static void OutputPlainHexCSP(int property, WCHAR * value1, WCHAR * value2,
                              FILE * hFile)
{
    static WCHAR s_commaSpace[] = { ',', ' ', 0 };
    OutputPlainPropertyTitle(property, hFile);
    OutputHexString(hFile, (unsigned int) lstrtolW(value1, NULL, 10));
    OutputWString(hFile, s_commaSpace);
    OutputHexString(hFile, (unsigned int) lstrtolW(value2, NULL, 10));
    OutputNewline(hFile);
}


/* WINE_FIXME: Function fixed, but using a hack */
static WCHAR *EncodeStringToXML(WCHAR * str)
{
    DWORD str_len = 0;
    DWORD xmlstr_len = 0;
    WCHAR *str_ptr = NULL;
    WCHAR *xmlstr = NULL;
    WCHAR *xmlstr_ptr = NULL;
    static WCHAR lessthan[] = { '&', 'l', 't', ';', 0 };
    static WCHAR greaterthan[] = { '&', 'g', 't', ';', 0 };
    static const DWORD ltLen = 4;
    static const DWORD gtLen = 4;

    /* Nothing needs to be done for empty strings */
    if (str == NULL || *str == '\0')
        return str;

    /* Each character that will need to be encoded will require extra space in the
       encoded string. */
    xmlstr_len = 0;
    for (str_ptr = str; *str_ptr != '\0'; str_ptr++) {
        if (*str_ptr == '&') {
            break;
        } else if (*str_ptr == '<') {
            xmlstr_len += ltLen;
        } else if (*str_ptr == '>') {
            xmlstr_len += gtLen;
        } else {
            xmlstr_len++;
        }
    }

    /* If the encoded string length and unencoded string length are the same
       then the string does not need any substitution (every substiution requires
       at least 3 more characters) */
    if (xmlstr_len == str_len)
        return str;

    /* If we got here, at least one character needs encoding, so allocate
       new storage big enough to hold the encoded string */
    xmlstr = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (xmlstr_len + 1));

    /* Convert the string to XML encoding */
    for (xmlstr_ptr = xmlstr, str_ptr = str; *str_ptr != '\0'; str_ptr++) {
        if (*str_ptr == '&') {
            break;
        } else if (*str_ptr == '<') {
            lstrcpyW(xmlstr_ptr, lessthan);
            xmlstr_ptr += ltLen;
        } else if (*str_ptr == '>') {
            lstrcpyW(xmlstr_ptr, greaterthan);
            xmlstr_ptr += gtLen;
        } else {
            *xmlstr_ptr = *str_ptr;
            xmlstr_ptr++;
        }
    }
    *xmlstr_ptr = '\0';
    return xmlstr;
}

/*
 Output a property value pair in the XML format, which looks like:

 <property name>value</property name>

 For example:

 <MachineName>drew-mbp</MachineName>
 */
static void OutputXMLProperty(int property, WCHAR * value, FILE * hFile)
{
    OutputNodeBegin(property, hFile);
    OutputWString(hFile, EncodeStringToXML(value));
    OutputNodeEnd(property, hFile);
}

/*
 Turn boolean string "True" and "False" into "1" and "0" respectively
 */
static WCHAR *BoolStringToNumString(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR s_zero[] = { '0', 0 };
    static WCHAR s_one[] = { '1', 0 };
    WCHAR boolFalse[MAX_STRING_LEN];
    WCHAR boolTrue[MAX_STRING_LEN];

    if (!value)
        return NULL;

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_TRUE, boolTrue, MAX_STRING_LEN);
        load = FALSE;
    }

    if (lstrcmpW(value, boolFalse) == 0)
        return s_zero;
    else if (lstrcmpW(value, boolTrue) == 0)
        return s_one;
    else
        return value;
}

/*
 Turn boolean string "True" and "False" into boolean
 */
static BOOL BoolStringToBoolean(WCHAR * value)
{
    static BOOL load = TRUE;
    WCHAR boolFalse[MAX_STRING_LEN];
    WCHAR boolTrue[MAX_STRING_LEN];

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_TRUE, boolTrue, MAX_STRING_LEN);
        load = FALSE;
    }

    if (!value || (lstrcmpW(value, boolFalse) == 0))
        return FALSE;
    else if (lstrcmpW(value, boolTrue) == 0)
        return TRUE;
    else
        return FALSE;
}

/*
 Turn boolean string "True" and "False" into "Yes" and "No" respectively
 */
static WCHAR *BoolStringToReadableString(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR boolFalse[MAX_STRING_LEN];
    static WCHAR boolTrue[MAX_STRING_LEN];
    static WCHAR no[MAX_STRING_LEN];
    static WCHAR yes[MAX_STRING_LEN];

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_TRUE, boolTrue,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_NO, no, MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_YES, yes, MAX_STRING_LEN);
        load = FALSE;
    }

    if (!value || (lstrcmpW(value, boolFalse) == 0))
        return no;
    else if (lstrcmpW(value, boolTrue) == 0)
        return yes;
    else
        return value;
}

static WCHAR *BoolStringToOkErrorString(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR boolFalse[MAX_STRING_LEN];
    static WCHAR boolTrue[MAX_STRING_LEN];
    static WCHAR ok[MAX_STRING_LEN];
    static WCHAR error[MAX_STRING_LEN];

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_TRUE, boolTrue,
                    MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_OK, ok, MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_ERROR, error, MAX_STRING_LEN);
        load = FALSE;
    }

    if (!value || (lstrcmpW(value, boolFalse) == 0))
        return error;
    else if (lstrcmpW(value, boolTrue) == 0)
        return ok;
    else
        return value;
}

static WCHAR *NumOrBoolStringCheckNotApplicable(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR boolFalse[MAX_STRING_LEN];
    static WCHAR s_notApplicable[] = { 'n', '/', 'a', 0 };

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                    MAX_STRING_LEN);
        load = FALSE;
    }

    if (value && ((*value == '\0') || (*value == '0')
        || (lstrcmpW(value, boolFalse) == 0)))
        return s_notApplicable;
    else
        return value;
}

/*
 Turn error strings that are empty into "OK"
*/
static WCHAR *PreprocessRegistryStringForXML(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR ok[MAX_STRING_LEN];
    static WCHAR errors[MAX_STRING_LEN];
    static WCHAR colonSpace[] = { ':', ' ', 0 };
    DWORD size;
    WCHAR *output;

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_OK, ok, MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_REGISTRY_ERRORS_FOUND,
                    errors, MAX_STRING_LEN);
        load = FALSE;
    }

    if (value && (*value == '\0'))
        return ok;
    else {
        size = sizeof(WCHAR) * (lstrlenW(errors) + lstrlenW(colonSpace) + lstrlenW(value) + 1);
        output = HeapAlloc(GetProcessHeap(), 0, size);
        lstrcpyW(output, errors);
        lstrcatW(output, colonSpace);
        lstrcatW(output, value);
        return output;
    }
}

/*
 Turn error strings that are empty into "OK"
*/
static WCHAR *PreprocessRegistryStringForPlain(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR ok[MAX_STRING_LEN];
    static WCHAR errors[MAX_STRING_LEN];
    static WCHAR newline[] = { '\n', 0 };
    DWORD size;
    WCHAR *output;

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_OK, ok, MAX_STRING_LEN);
        LoadStringW(g_hinstance, STRING_COMMON_REGISTRY_ERRORS_FOUND,
                    errors, MAX_STRING_LEN);
        load = FALSE;
    }

    if (value && (*value == '\0'))
        return ok;
    else {
        size = sizeof(WCHAR) * (lstrlenW(errors) + lstrlenW(newline) + lstrlenW(value) + 1);
        output = HeapAlloc(GetProcessHeap(), 0, size);
        lstrcpyW(output, errors);
        lstrcatW(output, newline);
        lstrcatW(output, value);
        return output;
    }
}

/*
 Turn boolean string "True" and "False" into "Yes" and "No" respectively
 */
static WCHAR *NumStringToReadableString(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR s_negOne[] = { '-', '1', 0 };
    static WCHAR unknown[MAX_STRING_LEN];

    if (!value)
        return NULL;

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_UNKNOWN, unknown,
                    MAX_STRING_LEN);
        load = FALSE;
    }

    if (lstrcmpW(value, s_negOne) == 0)
        return unknown;
    else
        return value;
}

static WCHAR *StringExistenceCheck(WCHAR * value)
{
    static BOOL load = TRUE;
    static WCHAR none[MAX_STRING_LEN];

    if (load) {
        LoadStringW(g_hinstance, STRING_COMMON_NONE, none, MAX_STRING_LEN);
        load = FALSE;
    }

    if (!value || *value == '\0')
        return none;
    else
        return value;
}

/* Returns characters before and including first newline in input */
static WCHAR *TakeBeforeNewline(WCHAR * input)
{
    WCHAR *firstNewline = NULL;
    WCHAR *index;
    WCHAR *output;

    if (input == NULL)
        return input;

    index = input;
    while (*index != '\0') {
        if (*index == '\n') {
            firstNewline = index;
            break;
        }
        index += 1;
    }

    if (firstNewline == NULL)
        return input;

    output = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(input) + 1));
    index = input;
    while (index != firstNewline) {
        *output = *index;
        index += 1;
        output += 1;
    }
    *output = '\n';             /* Keeps newline as original dxdiag does */
    *(output + 1) = '\0';

    return output;
}

static WCHAR *RemoveFilePath(WCHAR * fullFilePath)
{
    WCHAR *filename = NULL;
    WCHAR *current = fullFilePath;

    if ((!fullFilePath) || (*fullFilePath == '\0'))
        return fullFilePath;

    while (*current != '\0') {
        if (*current == '\\') {
            filename = current;
        }
        current = current + 1;
    }

    if (filename && (filename != (current - 1))) {
        return (filename + 1);
    }

    return fullFilePath;
}

static WCHAR *AppendBytesString(WCHAR * bytesNum)
{
    static WCHAR s_bytes[] = { ' ', 'b', 'y', 't', 'e', 's', 0 };
    WCHAR *output;
    DWORD size;

    size = sizeof(WCHAR) * (lstrlenW(bytesNum) + lstrlenW(s_bytes) + 1);

    output = HeapAlloc(GetProcessHeap(), 0, size);
    lstrcpyW(output, bytesNum);
    lstrcatW(output, s_bytes);

    return output;
}

/*    FILE OUTPUT FUNCTIONS    */
static void OutputPlainDeinterlaceCapProperty(DxDiag_DXVADeinterlaceCap *
                                              dxvaDeinterlaceCap,
                                              FILE * hFile)
{
    OutputPlainStringTitle(dxvaDeinterlaceCap->szGuid, hFile);

    OutputPlainPropertyEqualsTitle
        (TXT_DISPLAYDEVICES_DEINTERLACECAP_FORMAT, hFile);
    OutputChar(hFile, '(');
    OutputWString(hFile, dxvaDeinterlaceCap->szD3DInputFormat);
    OutputChar(hFile, ',');
    OutputWString(hFile, dxvaDeinterlaceCap->szD3DOutputFormat);
    OutputChar(hFile, ')');
    OutputChar(hFile, ' ');

    OutputPlainPropertyEqualsTitle
        (TXT_DISPLAYDEVICES_DEINTERLACECAP_FRAMES, hFile);
    OutputChar(hFile, '(');
    OutputWString(hFile, dxvaDeinterlaceCap->dwNumPreviousOutputFrames);
    OutputChar(hFile, ',');
    OutputWString(hFile, dxvaDeinterlaceCap->dwNumForwardRefSamples);
    OutputChar(hFile, ',');
    OutputWString(hFile, dxvaDeinterlaceCap->dwNumBackwardRefSamples);
    OutputChar(hFile, ')');
    OutputChar(hFile, ' ');

    OutputPlainPropertyEqualsTitle(TXT_DISPLAYDEVICES_DEINTERLACECAP_CAPS,
                                   hFile);
    OutputWString(hFile, dxvaDeinterlaceCap->szCaps);
    OutputNewline(hFile);
}

static void OutputDirectMusicPortHeader(FILE * hFile)
{
    static WCHAR s_spacing[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0 };
    OutputWString(hFile, s_spacing);
}

static void OutputDirectMusicPortForPlain(DxDiag_DirectMusicPort * port, FILE * hFile)
{
    static BOOL load = TRUE;
    static WCHAR s_commaSpace[] = { ',', ' ', 0 };
    static WCHAR software[MAX_STRING_LEN];
    static WCHAR hardware[MAX_STRING_LEN];
    static WCHAR kernelMode[MAX_STRING_LEN];
    static WCHAR notKernelMode[MAX_STRING_LEN];
    static WCHAR input[MAX_STRING_LEN];
    static WCHAR output[MAX_STRING_LEN];
    static WCHAR dls[MAX_STRING_LEN];
    static WCHAR noDLS[MAX_STRING_LEN];
    static WCHAR internal[MAX_STRING_LEN];
    static WCHAR external[MAX_STRING_LEN];
    static WCHAR defaultPort[MAX_STRING_LEN];

    if (load) {
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_SOFTWARE, software, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_HARDWARE, hardware, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_KERNELMODE, kernelMode, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_NOTKERNELMODE, notKernelMode, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_INPUT, input, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_OUTPUT, output, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_DLS, dls, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_NODLS, noDLS, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_INTERNAL, internal, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_EXTERNAL, external, MAX_STRING_LEN);
        LoadStringW(g_hinstance, TXT_DIRECTMUSIC_DEFAULTPORT, defaultPort, MAX_STRING_LEN);
        load = FALSE;
    }

    OutputWString(hFile, port->szDescription);
    OutputWString(hFile, s_commaSpace);

    if (BoolStringToBoolean(port->bSoftware))
        OutputWString(hFile, software);
    else
        OutputWString(hFile, hardware);

    OutputChar(hFile, ' ');
    if (BoolStringToBoolean(port->bKernelMode))
        OutputInParentheses(kernelMode, hFile);
    else
        OutputInParentheses(notKernelMode, hFile);

    OutputWString(hFile, s_commaSpace);
    if (BoolStringToBoolean(port->bOutputPort))
        OutputWString(hFile, output);
    else
        OutputWString(hFile, input);

    OutputWString(hFile, s_commaSpace);
    if (BoolStringToBoolean(port->bUsesDLS))
        OutputWString(hFile, dls);
    else
        OutputWString(hFile, noDLS);

    OutputWString(hFile, s_commaSpace);
    if (BoolStringToBoolean(port->bExternal))
        OutputWString(hFile, external);
    else
        OutputWString(hFile, internal);

    if (BoolStringToBoolean(port->bDefaultPort)) {
        OutputWString(hFile, s_commaSpace);
        OutputWString(hFile, defaultPort);
    }

    OutputNewline(hFile);
}

static void OutputPlainDirectXFileProperty(DxDiag_DirectXFile * file, FILE * hFile)
{
    static WCHAR s_bytes[] = { ' ', 'b', 'y', 't', 'e', 's', 0 };
    /* WINE_FIXME: move missing and fileMissing into resource file? */
    static WCHAR s_missing[] = { ' ', 'm', 'i', 's', 's', 'i', 'n', 'g', '!', 0 };
    static WCHAR s_fileMissing [] = { '<', 'F', 'i', 'l', 'e', ' ', 'M', 'i', 's', 's', 'i', 'n', 'g', '>', 0 };
    static CHAR space = ' ';

    OutputPlainStringTitle(file->szName, hFile);

    if (lstrcmpW(file->szVersion, s_fileMissing) == 0)
        OutputWString(hFile, s_missing);
    else {
    OutputWString(hFile, file->szVersion);
    OutputChar(hFile, space);
    OutputWString(hFile, file->szLanguageEnglish);
    OutputChar(hFile, space);
    OutputWString(hFile, file->szAttributes);
    OutputChar(hFile, space);
    OutputWString(hFile, file->szDatestampEnglish);
    OutputChar(hFile, space);
    OutputWString(hFile, file->lNumBytes);
    OutputWString(hFile, s_bytes);
    }
    OutputNewline(hFile);
}

static void OutputPlainDirectShowFilterProperty(DxDiag_DirectShowFilter * filter, FILE * hFile)
{
    static CHAR comma = ',';

    OutputWString(hFile, filter->szName);
    OutputChar(hFile, comma);
    /* WINE_FIXME: native dxdiag displays hex strings with 8 characters for filters, e.g. "0x00400000" */
    OutputHexString(hFile, (unsigned int) lstrtolW(filter->dwMerit, NULL, 10));
    OutputChar(hFile, comma);
    OutputWString(hFile, filter->dwInputs);
    OutputChar(hFile, comma);
    OutputWString(hFile, filter->dwOutputs);
    OutputChar(hFile, comma);
    OutputWString(hFile, RemoveFilePath(filter->szFileName));
    OutputChar(hFile, comma);
    OutputWString(hFile, filter->szFileVersion);
    OutputNewline(hFile);
}

/* WINE_FIXME: NOT IMPLEMENTED YET */
static void OutputDriversForPlain(FILE * hFile, DxDiag_Driver ** drivers,
                             int numDrivers, int depth)
/* depth used only for tree output, set to -1 for no tree */
/* tree output has various restrictions - see code */
{
    static WCHAR s_bytes[] = { ' ', 'b', 'y', 't', 'e', 's', 0 };
    static WCHAR s_commaSpace[] = { ',', ' ', 0 };
    static WCHAR s_barSpace[] = { '|', ' ', 0 };
    DxDiag_Driver *driver;
    DWORD driverNum;
    DWORD treeDepth;

    if (!drivers)
        return;

    for (driverNum = 0; driverNum < numDrivers; driverNum++) {
        driver = drivers[driverNum];

        /* output tree characters */
        if (depth >= 0)
            for(treeDepth = 0; treeDepth <= depth; treeDepth++)
                OutputWString(hFile, s_barSpace);

        OutputPlainPropertyTitle(TXT_DRIVERS_DRIVER, hFile);

        /* title is filename without path for tree */
        if (depth >= 0) {
            OutputWString(hFile, RemoveFilePath(driver->szPath));
            OutputWString(hFile, s_commaSpace);
        } else {
            OutputWString(hFile, driver->szPath);
            OutputWString(hFile, s_commaSpace);
            /* version and language is only for non-tree output */
            if (driver->szVersion) {
                OutputWString(hFile, driver->szVersion);
                OutputChar(hFile, ' ');
                OutputInParentheses(driver->szLanguageEnglish, hFile);
                OutputWString(hFile, s_commaSpace);
            }
        }

        OutputWString(hFile, driver->szDatestampEnglish);
        OutputWString(hFile, s_commaSpace);
        OutputWString(hFile, driver->lNumBytes);
        OutputWString(hFile, s_bytes);
        OutputNewline(hFile);
    }
}

static void OutputTreeHeadMarker(FILE * hFile, int depth)
{
    static WCHAR s_barSpace[] = { '|', ' ', 0 };
    static WCHAR s_plusSpace[] = { '+', ' ', 0 };
    static WCHAR s_plusDash[] = { '+', '-', 0 };
    DWORD treeDepth;

    for(treeDepth = depth; treeDepth > 1; treeDepth--) {
        OutputWString(hFile, s_barSpace);
    }
    if (depth > 0)
        OutputWString(hFile, s_plusDash);
    OutputWString(hFile, s_plusSpace);
}

static void OutputTreeBodyMarker(FILE * hFile, int depth)
{
    static WCHAR s_barSpace[] = { '|', ' ', 0 };
    DWORD treeDepth;

    for(treeDepth = 0; treeDepth <= depth; treeDepth++) {
        OutputWString(hFile, s_barSpace);
    }
}

static void OutputInputRelatedDeviceForPlain(FILE * hFile, DxDiag_DirectInputRelatedDevice * inputRelatedDevice, int depth)
{
    DWORD inputRelatedDeviceNum;

    if (depth < 0)
        return;

    /* Properties */
    OutputTreeHeadMarker(hFile, depth);
    OutputWString(hFile, inputRelatedDevice->szDescription);

    if ((inputRelatedDevice->dwVendorID && (*inputRelatedDevice->dwVendorID != '0')) ||
        (inputRelatedDevice->dwProductID && (*inputRelatedDevice->dwProductID != '0')))
        OutputTreeBodyMarker(hFile, depth);
        OutputPlainHexCSP(TXT_DIRECTINPUT_VENDORPRODUCTID,
                      inputRelatedDevice->dwVendorID,
                      inputRelatedDevice->dwProductID,
                      hFile);

    OutputTreeBodyMarker(hFile, depth);
    OutputPlainProperty(TXT_DIRECTINPUT_MATCHINGDEVICEID,
                        inputRelatedDevice->szMatchingDeviceId,
                        hFile);

    /* WINE_FIXME: Don't know what order upperfilters and lowerfilters are in */
    if (inputRelatedDevice->szUpperFilters) {
    OutputTreeBodyMarker(hFile, depth);
    OutputPlainProperty(TXT_DIRECTINPUT_UPPERFILTERS,
                        inputRelatedDevice->szUpperFilters,
                        hFile);
    }

    if (inputRelatedDevice->szLowerFilters) {
    OutputTreeBodyMarker(hFile, depth);
    OutputPlainProperty(TXT_DIRECTINPUT_LOWERFILTERS,
                        inputRelatedDevice->szLowerFilters,
                        hFile);
    }

    if (inputRelatedDevice->szService) {
    OutputTreeBodyMarker(hFile, depth);
    OutputPlainProperty(TXT_DIRECTINPUT_SERVICE,
                        inputRelatedDevice->szService,
                        hFile);
    }

    /* Drivers */
    if (inputRelatedDevice->drivers != NULL) {
        OutputDriversForPlain(hFile, inputRelatedDevice->drivers,
                                  inputRelatedDevice->numDrivers, depth);
    }

    /* Input Related Devices */
    if (inputRelatedDevice->directInputRelatedDevices != NULL) {
        for (inputRelatedDeviceNum = 0; inputRelatedDeviceNum
             < inputRelatedDevice->numDirectInputRelatedDevices;
             inputRelatedDeviceNum++) {

            if (inputRelatedDeviceNum < inputRelatedDevice->numDirectInputRelatedDevices - 1) {
                OutputTreeBodyMarker(hFile, depth);
                OutputNewline(hFile);
            }

            OutputInputRelatedDeviceForPlain(hFile,
                                             inputRelatedDevice->
                                             directInputRelatedDevices
                                             [inputRelatedDeviceNum], depth+1);
        }

    }

}

static void OutputPlainFile(FILE * hFile)
{
    /* CONTAINERS AND ITERATORS */
    /* SystemInfo */
    DxDiag_SystemInfo *systemInfo = g_dxdiagcom_root.systemInfo;

    /* DisplayDevices */
    DxDiag_DisplayDevice **displayDevices =
        g_dxdiagcom_root.displayDevices;
    DxDiag_DisplayDevice *displayDevice = NULL;
    DWORD displayDeviceNum;

    DxDiag_DXVADeinterlaceCap **dxvaDeinterlaceCaps = NULL;
    DxDiag_DXVADeinterlaceCap *dxvaDeinterlaceCap = NULL;
    DWORD dxvaDeinterlaceCapNum;

    /* SoundDevices */
    DxDiag_SoundDevice **soundDevices = g_dxdiagcom_root.soundDevices;
    DxDiag_SoundDevice *soundDevice = NULL;
    DWORD soundDeviceNum;

    /* SoundCaptureDevices */
    DxDiag_SoundCaptureDevice **soundCaptureDevices =
        g_dxdiagcom_root.soundCaptureDevices;
    DxDiag_SoundCaptureDevice *soundCaptureDevice = NULL;
    DWORD soundCaptureDeviceNum;

    /* DirectMusic */
    DxDiag_DirectMusic *directMusic = g_dxdiagcom_root.directMusic;
    DxDiag_DirectMusicPort **ports = directMusic->directMusicPorts;
    DxDiag_DirectMusicPort *port = NULL;
    DWORD portNum;

    /* DirectInput */
    DxDiag_DirectInput *directInput = g_dxdiagcom_root.directInput;

    DxDiag_DirectInputDevice **directInputDevices =
        directInput->directInputDevices;
    DxDiag_DirectInputDevice *directInputDevice = NULL;
    DWORD directInputDeviceNum;

    DxDiag_DirectInputRelatedDevice **usbRoot =
        directInput->directInputUSBs;
    DxDiag_DirectInputRelatedDevice *usbDevice = NULL;
    DWORD usbDeviceNum;

    DxDiag_DirectInputRelatedDevice **gameportDevices =
        directInput->directInputGamePorts;
    DxDiag_DirectInputRelatedDevice *gameportDevice = NULL;
    DWORD gameportDeviceNum;

    DxDiag_DirectInputRelatedDevice **ps2Devices =
        directInput->directInputPs2Devices;
    DxDiag_DirectInputRelatedDevice *ps2Device = NULL;
    DWORD ps2DeviceNum;

    /* DirectPlay */
    DxDiag_DirectPlay *directPlay = g_dxdiagcom_root.directPlay;

    DxDiag_DirectPlaySp **directPlaySPs = directPlay->directPlaySps;
    DxDiag_DirectPlaySp *directPlaySP = NULL;
    DWORD directPlaySPNum;

    DxDiag_DirectPlayAdapter **directPlayAdapters =
        directPlay->directPlayAdapters;
    DxDiag_DirectPlayAdapter *directPlayAdapter = NULL;
    DWORD directPlayAdapterNum;

    /* WINE_FIXME: Add LobbyableApps and VoiceCodecs once code done */

    /* LogicalDisks */
    DxDiag_LogicalDisk **logicalDisks = g_dxdiagcom_root.logicalDisks;
    DxDiag_LogicalDisk *logicalDisk = NULL;
    DWORD logicalDiskNum;

    /* SystemDevices */
    DxDiag_SystemDevice **systemDevices = g_dxdiagcom_root.systemDevices;
    DxDiag_SystemDevice *systemDevice = NULL;
    DWORD systemDeviceNum;

    /* DirectXFiles */
    DxDiag_DirectXFiles *directXFiles = g_dxdiagcom_root.directXFiles;
    DxDiag_DirectXFile *file = NULL;
    DWORD fileNum;

    /* DirectShow */
    DxDiag_DirectShowFilter **directShow =
        g_dxdiagcom_root.directShowFilters;
    DxDiag_DirectShowFilter *filter = NULL;
    DWORD filterNum;

    /* STRINGS */
    /* DirectMusic */
    static WCHAR s_notApplicable[] = { 'n', '/', 'a', 0 };
    static WCHAR s_commaSpace[] = { ',', ' ', 0 };
    static WCHAR s_spaceDashSpace[] = { ' ', '-', ' ', 0 };
    static WCHAR boolFalse[MAX_STRING_LEN];

    /* STRING INITIALIZATION */
    LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                MAX_STRING_LEN);

    /* PLAIN TEXT FILE POPULATION */
    /* SystemInfo */
    OutputSectionHeading(TXT_SYSTEM, hFile);
    OutputPlainProperty(TXT_SYSTEM_TIME, systemInfo->szTimeEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_MACHINENAME,
                        systemInfo->szMachineNameLocalized, hFile);
    OutputPlainProperty(TXT_SYSTEM_OS, systemInfo->szOSExLongEnglish,
                        hFile);
    OutputPlainProperty(TXT_SYSTEM_LANGUAGE,
                        systemInfo->szLanguagesEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_MANUFACTURER,
                        systemInfo->szSystemManufacturerEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_MODEL,
                        systemInfo->szSystemModelEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_BIOS, systemInfo->szBIOSEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_PROCESSOR,
                        systemInfo->szProcessorEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_MEMORY,
                        systemInfo->szPhysicalMemoryEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_PAGEFILE,
                        systemInfo->szPageFileLocalized, hFile);
    OutputPlainProperty(TXT_SYSTEM_WINDIR, systemInfo->szWindowsDir,
                        hFile);
    OutputPlainProperty(TXT_SYSTEM_DXVERSION,
                        systemInfo->szDirectXVersionLongEnglish, hFile);
    OutputPlainProperty(TXT_SYSTEM_DXSETUPPARAMS,
                        systemInfo->szSetupParamEnglish, hFile);
    OutputPlainSSP(TXT_SYSTEM_DXDIAGVER, systemInfo->szDxDiagVersion,
                   LSTRING_COMMON_WINE_FIXME,
                   LSTRING_COMMON_WINE_FIXME, hFile);

    /* DxDiagNotes */
    OutputSectionHeading(TXT_DXDIAGNOTES, hFile);
    OutputPlainProperty(TXT_DXDIAGNOTES_DXFILESTAB,
                        TakeBeforeNewline
                        (directXFiles->szDXFileNotesEnglish), hFile);

    /* WINE_FIXME: Need to add number after "Display Tab", e.g. "Display Tab 1" */
    for (displayDeviceNum = 0; displayDeviceNum
         < g_dxdiagcom_root.numDisplayDevices; displayDeviceNum++) {
        displayDevice = displayDevices[displayDeviceNum];
        OutputPlainProperty(TXT_DXDIAGNOTES_DISPLAYTAB,
                            TakeBeforeNewline
                            (displayDevice->szNotesEnglish), hFile);
    }

    /* WINE_FIXME: Need to add number after "Sound Tab", e.g. "Sound Tab 1" */
    for (soundDeviceNum = 0; soundDeviceNum
         < g_dxdiagcom_root.numSoundDevices; soundDeviceNum++) {
        soundDevice = soundDevices[soundDeviceNum];
        OutputPlainProperty(TXT_DXDIAGNOTES_SOUNDTAB,
                            TakeBeforeNewline(soundDevice->szNotesEnglish),
                            hFile);
    }

    OutputPlainProperty(TXT_DXDIAGNOTES_MUSICTAB,
                        TakeBeforeNewline(directMusic->szNotesEnglish),
                        hFile);
    OutputPlainProperty(TXT_DXDIAGNOTES_INPUTTAB,
                        TakeBeforeNewline
                        (directInput->szInputNotesEnglish), hFile);
    OutputPlainProperty(TXT_DXDIAGNOTES_NETWORKTAB,
                        TakeBeforeNewline
                        (directPlay->szNetworkNotesEnglish), hFile);

    /* DxDiagDebugLevels - WINE_FIXME */
    OutputSectionHeading(TXT_DXDEBUGLEVELS, hFile);
    OutputPlainDebugLevelProperty(TXT_DXDEBUGLEVELS_DIRECT3D,
                                  systemInfo->nD3DDebugLevel,
                                  LSTRING_COMMON_WINE_FIXME,
                                  LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTDRAW,
                        LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTINPUT,
                        LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTMUSIC,
                        LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTPLAY,
                        LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTSOUND,
                        LSTRING_COMMON_WINE_FIXME, hFile);
    OutputPlainProperty(TXT_DXDEBUGLEVELS_DIRECTSHOW,
                        LSTRING_COMMON_WINE_FIXME, hFile);

    /* DisplayDevices - WINE_FIXME */
    OutputSectionHeading(TXT_DISPLAYDEVICES, hFile);
    for (displayDeviceNum = 0; displayDeviceNum
         < g_dxdiagcom_root.numDisplayDevices; displayDeviceNum++) {
        displayDevice = displayDevices[displayDeviceNum];
        OutputPlainProperty(TXT_DISPLAYDEVICES_CARDNAME,
                            displayDevice->szDescription, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_MANUFACTURER,
                            displayDevice->szManufacturer, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_CHIPTYPE,
                            displayDevice->szChipType, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DACTYPE,
                            displayDevice->szDACType, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DEVICEKEY,
                            displayDevice->szKeyDeviceID, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DISPLAYMEMORY,
                            displayDevice->szDisplayMemoryEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_CURRENTMODE,
                            displayDevice->szDisplayModeEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_MONITOR,
                            displayDevice->szMonitorName, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_MONITORMAXRES,
                            displayDevice->szMonitorMaxRes, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DRIVERNAME,
                            displayDevice->szDriverName, hFile);
        OutputPlainDriverVersionProperty(TXT_DISPLAYDEVICES_DRIVERVERSION,
                                         displayDevice->szDriverVersion,
                                         displayDevice->
                                         szDriverLanguageEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DDIVERSION,
                            displayDevice->szDDIVersionEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DRIVERATTRIBUTES,
                            displayDevice->szDriverAttributes, hFile);
        OutputPlainCSP(TXT_DISPLAYDEVICES_DRIVERDATESIZE,
                       displayDevice->szDriverDateEnglish,
                       AppendBytesString(displayDevice->lDriverSize),
                       hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DRIVERWHQLLOGO,
                            BoolStringToReadableString(displayDevice->
                                                       bDriverSigned),
                            hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_WHQLDATESTAMP,
                            displayDevice->szDriverSignDate, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_VDD, displayDevice->szVdd,
                            hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_MINIVDD,
                            displayDevice->szMiniVdd, hFile);
        OutputPlainCSP(TXT_DISPLAYDEVICES_MINIVDDDATE,
                       displayDevice->szMiniVddDateEnglish,
                       AppendBytesString(displayDevice->lMiniVddSize),
                       hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DEVICEIDENTIFIER,
                            displayDevice->szDeviceIdentifier, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_VENDORID,
                            displayDevice->szVendorId, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DEVICEID,
                            displayDevice->szDeviceId, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_SUBSYSID,
                            displayDevice->szSubSysId, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_REVISIONID,
                            displayDevice->szRevisionId, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_VIDEOACCEL,
                            displayDevice->szDXVAModes, hFile);

        /* DeinterlaceCaps */
        OutputPlainPropertyTitle(TXT_DISPLAYDEVICES_DEINTERLACECAPS,
                                 hFile);
        dxvaDeinterlaceCaps = displayDevice->dxvaDeinterlaceCaps;
        if (displayDevice->numDxvaDeinterlaceCaps <= 0)
            OutputWString(hFile, s_notApplicable);
        for (dxvaDeinterlaceCapNum = 0; dxvaDeinterlaceCapNum
             < displayDevice->numDxvaDeinterlaceCaps;
             dxvaDeinterlaceCapNum++) {
            /* FOR EACH DEINTERLACE CAP */
            dxvaDeinterlaceCap =
                dxvaDeinterlaceCaps[dxvaDeinterlaceCapNum];

            OutputPlainDeinterlaceCapProperty(dxvaDeinterlaceCap, hFile);
        }
        OutputNewline(hFile);

        /* Special registry format for DisplayDevice */
        OutputNewline(hFile);
        OutputPlainPropertyTitle(TXT_DISPLAYDEVICES_REGISTRY, hFile);
        OutputNewline(hFile);
        OutputWString(hFile, displayDevice->szRegHelpText);
        OutputNewline(hFile);

        OutputPlainProperty(TXT_DISPLAYDEVICES_DDRAWSTATUS,
                            displayDevice->szDDStatusEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_D3DSTATUS,
                            displayDevice->szD3DStatusEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_AGPSTATUS,
                            displayDevice->szAGPStatusEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_DDRAWTESTRESULT,
                            displayDevice->szTestResultDDEnglish, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_D3D7TESTRESULT,
                            displayDevice->szTestResultD3D7English, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_D3D8TESTRESULT,
                            displayDevice->szTestResultD3D8English, hFile);
        OutputPlainProperty(TXT_DISPLAYDEVICES_D3D9TESTRESULT,
                            displayDevice->szTestResultD3D9English, hFile);

        if (displayDeviceNum < g_dxdiagcom_root.numDisplayDevices - 1)
            OutputNewline(hFile);
    }

    /* SoundDevices */
    OutputSectionHeading(TXT_DIRECTSOUND_SOUNDDEVICES, hFile);

    for (soundDeviceNum = 0;
         soundDeviceNum < g_dxdiagcom_root.numSoundDevices;
         soundDeviceNum++) {
        /* FOR EACH SOUND DEVICE */
        soundDevice = soundDevices[soundDeviceNum];

        OutputPlainProperty(TXT_DIRECTSOUND_DESCRIPTION,
                            soundDevice->szDescription, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DEFAULTSOUNDPLAYBACK,
                            BoolStringToReadableString
                            (soundDevice->bDefaultSoundPlayback), hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DEFAULTVOICEPLAYBACK,
                            BoolStringToReadableString
                            (soundDevice->bDefaultVoicePlayback), hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_HARDWAREID,
                            soundDevice->szHardwareID, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_MANUFACTURERID,
                            soundDevice->szManufacturerID, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_PRODUCTID,
                            soundDevice->szProductID, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_TYPE, soundDevice->szType,
                            hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERNAME,
                            soundDevice->szDriverName, hFile);
        OutputPlainDriverVersionProperty(TXT_DIRECTSOUND_DRIVERVERSION,
                                         soundDevice->szDriverVersion,
                                         soundDevice->
                                         szDriverLanguageEnglish, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERATTRIBUTES,
                            soundDevice->szDriverAttributes, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERWHQLLOGO,
                            BoolStringToReadableString(soundDevice->
                                                       bDriverSigned),
                            hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_DRIVERDATESIZE,
                       soundDevice->szDriverDateLocalized,
                       AppendBytesString(soundDevice->lNumBytes), hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_OTHERFILES,
                            soundDevice->szOtherDrivers, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERPROVIDER,
                            soundDevice->szProvider, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_HWACCELLEVEL,
                            NumStringToReadableString(soundDevice->
                                                      lAccelerationLevel),
                            hFile);
        OutputPlainHexProperty(TXT_DIRECTSOUND_CAPFLAGS,
                               soundDevice->dwFlags,
                               hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_MINMAXSAMPLERATE,
                       soundDevice->dwMinSecondarySampleRate,
                       soundDevice->dwMaxSecondarySampleRate, hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_HWMIXBUFFERS,
                       soundDevice->dwMaxHwMixingStaticBuffers,
                       soundDevice->dwMaxHwMixingStreamingBuffers, hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_HW3DBUFFERS,
                       soundDevice->dwMaxHw3DStaticBuffers,
                       soundDevice->dwMaxHw3DStreamingBuffers, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_HWMEMORY,
                            soundDevice->dwTotalHwMemBytes, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_VOICEMANAGER,
                            BoolStringToReadableString(soundDevice->
                                                       bVoiceManager),
                            hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_EAX20,
                       BoolStringToReadableString(soundDevice->
                                                  bEAX20Listener),
                       BoolStringToReadableString(soundDevice->
                                                  bEAX20Source), hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_I3DL2,
                       BoolStringToReadableString(soundDevice->
                                                  bI3DL2Listener),
                       BoolStringToReadableString(soundDevice->
                                                  bI3DL2Source), hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_ZOOMFX,
                            BoolStringToReadableString(soundDevice->
                                                       bZoomFX), hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_REGISTRY,
                            PreprocessRegistryStringForPlain(soundDevice->
                                                             szRegHelpText),
                            hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_SOUNDTESTRESULT,
                            soundDevice->szTestResultLocalized, hFile);

        if (soundDeviceNum < g_dxdiagcom_root.numSoundDevices - 1)
            OutputNewline(hFile);
    }

    /* SoundCaptureDevices */
    OutputSectionHeading(TXT_DIRECTSOUND_SOUNDCAPTUREDEVICES, hFile);
    for (soundCaptureDeviceNum = 0; soundCaptureDeviceNum
         < g_dxdiagcom_root.numSoundCaptureDevices;
         soundCaptureDeviceNum++) {
        /* FOR EACH SOUNDCAPTURE DEVICE */
        soundCaptureDevice = soundCaptureDevices[soundCaptureDeviceNum];

        OutputPlainProperty(TXT_DIRECTSOUND_DESCRIPTION,
                            soundCaptureDevice->szDescription, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DEFAULTSOUNDCAPTURE,
                            BoolStringToReadableString(soundCaptureDevice->
                                                       bDefaultSoundRecording),
                            hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DEFAULTVOICECAPTURE,
                            BoolStringToReadableString(soundCaptureDevice->
                                                       bDefaultVoiceRecording),
                            hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERNAME,
                            soundCaptureDevice->szDriverName, hFile);
        OutputPlainDriverVersionProperty(TXT_DIRECTSOUND_DRIVERVERSION,
                                         soundCaptureDevice->
                                         szDriverVersion,
                                         soundCaptureDevice->
                                         szDriverLanguageEnglish, hFile);
        OutputPlainProperty(TXT_DIRECTSOUND_DRIVERATTRIBUTES,
                            soundCaptureDevice->szDriverAttributes, hFile);
        OutputPlainCSP(TXT_DIRECTSOUND_DRIVERDATESIZE,
                       soundCaptureDevice->szDriverDateLocalized,
                       AppendBytesString(soundCaptureDevice->lNumBytes),
                       hFile);
        OutputPlainHexProperty(TXT_DIRECTSOUND_CAPFLAGS,
                               soundCaptureDevice->dwFlags, hFile);
        OutputPlainHexProperty(TXT_DIRECTSOUND_FORMATFLAGS,
                               soundCaptureDevice->dwFormats, hFile);

        if (soundCaptureDeviceNum <
            g_dxdiagcom_root.numSoundCaptureDevices - 1)
            OutputNewline(hFile);
    }

    /* DirectMusic */
    OutputSectionHeading(TXT_DIRECTMUSIC, hFile);
    OutputPlainProperty(TXT_DIRECTMUSIC_DLSPATH, directMusic->szGMFilePath,
                        hFile);
    OutputPlainProperty(TXT_DIRECTMUSIC_DLSVERSION,
                        directMusic->szGMFileVersion, hFile);
    OutputPlainProperty(TXT_DIRECTMUSIC_ACCELERATION,
                        BoolStringToReadableString
                        (NumOrBoolStringCheckNotApplicable
                         (directMusic->bAccelerationEnabled)), hFile);

    OutputPlainPropertyTitle(TXT_DIRECTMUSIC_PORTS, hFile);
    for (portNum = 0; portNum < directMusic->numDirectMusicPorts;
         portNum++) {
        /* FOR EACH PORT */
        port = ports[portNum];

        if (portNum > 0)
            OutputDirectMusicPortHeader(hFile);

        OutputDirectMusicPortForPlain(port, hFile);
        /* END PORTS */
    }
    OutputPlainProperty(TXT_DIRECTMUSIC_REGISTRY,
                        PreprocessRegistryStringForPlain(directMusic->
                                                         szRegHelpText), hFile);
    OutputPlainProperty(TXT_DIRECTMUSIC_TESTRESULT,
                        directMusic->szTestResultEnglish, hFile);

    /* DirectInputDevices */
    OutputSectionHeading(TXT_DIRECTINPUT_DEVICES, hFile);

    for (directInputDeviceNum = 0; directInputDeviceNum
         < g_dxdiagcom_root.directInput->numDirectInputDevices;
         directInputDeviceNum++) {
        /* FOR EACH DIRECTINPUT DEVICE */
        directInputDevice = directInputDevices[directInputDeviceNum];

        OutputPlainProperty(TXT_DIRECTINPUT_DEVICENAME,
                            directInputDevice->szInstanceName, hFile);
        OutputPlainProperty(TXT_DIRECTINPUT_ATTACHED,
                            BoolStringToNumString(directInputDevice->
                                                  bAttached), hFile);

        /* These next two are Hex outputs, but need to account for Not Applicable */
        if ((*directInputDevice->dwVendorID == '0') && (*directInputDevice->dwProductID == '0')) {
            OutputPlainProperty(TXT_DIRECTINPUT_CONTROLLERID,
                                s_notApplicable, hFile);

            OutputPlainProperty(TXT_DIRECTINPUT_VENDORPRODUCTID,
                                s_notApplicable, hFile);

        } else {
            OutputPlainHexProperty(TXT_DIRECTINPUT_CONTROLLERID,
                                   directInputDevice->dwJoystickID, hFile);

            OutputPlainPropertyTitle(TXT_DIRECTINPUT_VENDORPRODUCTID, hFile);
            OutputPlainHexCSP(TXT_DIRECTINPUT_VENDORPRODUCTID, directInputDevice->dwVendorID,
                              directInputDevice->dwProductID, hFile);
        }

        if (!directInputDevice->szFFDriverName || (*directInputDevice->szFFDriverName == '\0')) {
            OutputPlainProperty(TXT_DIRECTINPUT_FFDRIVER, s_notApplicable,
                                hFile);
        } else {
            /* WINE_FIXME: Do not know what FFDriver format is, thus cannot implement */
        }

        OutputNewline(hFile);
    }

    OutputPlainProperty(TXT_DIRECTINPUT_POLLWITHINTERRUPT,
                        BoolStringToReadableString(directInput->bPollFlags), hFile);
    OutputPlainProperty(TXT_DIRECTINPUT_REGISTRY, PreprocessRegistryStringForPlain(directInput->szRegHelpText),
                        hFile);

    /* DirectInputUSBDevices */
    OutputSectionHeading(TXT_DIRECTINPUT_USBDEVICES, hFile);
    for (usbDeviceNum = 0; usbDeviceNum
         < directInput->numDirectInputUSBs; usbDeviceNum++) {
        usbDevice = usbRoot[usbDeviceNum];
        OutputInputRelatedDeviceForPlain(hFile, usbDevice, 0);
    }

    /* DirectInputGameportDevices */
    OutputSectionHeading(TXT_DIRECTINPUT_GAMEPORTDEVICES, hFile);
    for (gameportDeviceNum = 0; gameportDeviceNum
         < directInput->numDirectInputGamePorts; gameportDeviceNum++) {
        gameportDevice = gameportDevices[gameportDeviceNum];
        OutputInputRelatedDeviceForPlain(hFile, gameportDevice, 0);
    }

    /* DirectInputPS2Devices */
    OutputSectionHeading(TXT_DIRECTINPUT_PS2DEVICES, hFile);
    for (ps2DeviceNum = 0; ps2DeviceNum
         < directInput->numDirectInputPs2Devices; ps2DeviceNum++) {
        ps2Device = ps2Devices[ps2DeviceNum];
        OutputInputRelatedDeviceForPlain(hFile, ps2Device, 0);
    }

    /* DirectPlayServiceProviders */
    OutputSectionHeading(TXT_DIRECTPLAY_SERVICEPROVIDERS, hFile);
    for (directPlaySPNum = 0; directPlaySPNum
         < directPlay->numDirectPlaySps; directPlaySPNum++) {
        /* FOR EACH SERVICE PROVIDER */
        directPlaySP = directPlaySPs[directPlaySPNum];

        OutputPlainStringTitle(directPlaySP->szNameEnglish, hFile);
        OutputWString(hFile, s_spaceDashSpace);
        OutputPlainPropertyTitle(TXT_DIRECTPLAY_REGISTRY, hFile);
        OutputWString(hFile, BoolStringToOkErrorString(directPlaySP->bRegistryOK));
        OutputWString(hFile, s_commaSpace);
        OutputPlainPropertyTitle(TXT_DIRECTPLAY_FILE, hFile);
        OutputWString(hFile, directPlaySP->szFile);
        OutputChar(hFile, ' ');
        OutputInParentheses(directPlaySP->szVersionEnglish, hFile);
        OutputNewline(hFile);

        /* END SERVICE PROVIDER */
    }
    OutputNewline(hFile);

    /* VoiceWizardTests Custom Output */
    OutputPlainPropertyTitle(TXT_DIRECTPLAY_VOICEWIZARDTESTS, hFile);
    OutputPlainPropertyTitle(TXT_DIRECTPLAY_VOICEWIZARDTESTS_FULLDUPLEX, hFile);
    OutputWString(hFile, directPlay->szVoiceWizardFullDuplexTestEnglish);
    OutputWString(hFile, s_commaSpace);
    OutputPlainPropertyTitle(TXT_DIRECTPLAY_VOICEWIZARDTESTS_HALFDUPLEX, hFile);
    OutputWString(hFile, directPlay->szVoiceWizardHalfDuplexTestEnglish);
    OutputWString(hFile, s_commaSpace);
    OutputPlainPropertyTitle(TXT_DIRECTPLAY_VOICEWIZARDTESTS_MIC, hFile);
    OutputWString(hFile, directPlay->szVoiceWizardMicTestEnglish);
    OutputNewline(hFile);

    OutputPlainProperty(TXT_DIRECTPLAY_TESTRESULTS, directPlay->szTestResultEnglish,
                        hFile);
    OutputPlainProperty(TXT_DIRECTPLAY_REGISTRY, PreprocessRegistryStringForPlain(directPlay->szRegHelpText), hFile);

    /* DirectPlayAdapters */
    OutputSectionHeading(TXT_DIRECTPLAY_ADAPTERS, hFile);
    for (directPlayAdapterNum = 0; directPlayAdapterNum
         < directPlay->numDirectPlayAdapters; directPlayAdapterNum++) {
        /* FOR EACH ADAPTER */
        directPlayAdapter = directPlayAdapters[directPlayAdapterNum];

        OutputPlainDirectPlayAdapterProperty(directPlayAdapter->szSPNameEnglish,
                                             directPlayAdapter->szAdapterName,
                                             hFile);
    }

    /* DirectPlayVoiceCodecs */
    /* WINE_FIXME: Structure not yet known */
    OutputSectionHeading(TXT_DIRECTPLAY_VOICECODECS, hFile);

    /* DirectPlayLobbyableApps */
    /* WINE_FIXME: Structure not yet known */
    OutputSectionHeading(TXT_DIRECTPLAY_LOBBYABLEAPPS, hFile);

    /* LogicalDisks */
    if (g_dxdiagcom_root.numLogicalDisks > 0)
        OutputSectionHeading(TXT_LOGICALDISKS, hFile);
    for (logicalDiskNum = 0;
         logicalDiskNum < g_dxdiagcom_root.numLogicalDisks;
         logicalDiskNum++) {
        /* FOR EACH LOGICAL DISK */
        logicalDisk = logicalDisks[logicalDiskNum];

        OutputPlainProperty(TXT_LOGICALDISKS_DRIVELETTER, logicalDisk->szDriveLetter, hFile);
        if (logicalDisk->szFreeSpace)
            OutputPlainProperty(TXT_LOGICALDISKS_FREESPACE,
                                logicalDisk->szFreeSpace, hFile);
        if (logicalDisk->szMaxSpace)
            OutputPlainProperty(TXT_LOGICALDISKS_MAXSPACE,
                                logicalDisk->szMaxSpace, hFile);
        if (logicalDisk->szFileSystem)
            OutputPlainProperty(TXT_LOGICALDISKS_FILESYSTEM,
                                logicalDisk->szFileSystem, hFile);
        OutputPlainProperty(TXT_LOGICALDISKS_MODEL, logicalDisk->szModel,
                            hFile);

        /* Drivers */
        if (logicalDisk->drivers != NULL)
            OutputDriversForPlain(hFile, logicalDisk->drivers,
                                  logicalDisk->numDrivers, -1);

        if (logicalDiskNum <
            g_dxdiagcom_root.numLogicalDisks - 1)
            OutputNewline(hFile);
        /* END LOGICAL DISK */
    }

    /* SystemDevices */
    if (g_dxdiagcom_root.numSystemDevices > 0)
        OutputSectionHeading(TXT_SYSTEMDEVICES, hFile);
    for (systemDeviceNum = 0; systemDeviceNum
         < g_dxdiagcom_root.numSystemDevices; systemDeviceNum++) {
        /* FOR EACH SYSTEM DEVICE */
        systemDevice = systemDevices[systemDeviceNum];

        OutputPlainProperty(TXT_SYSTEMDEVICES_NAME,
                            systemDevice->szDescription, hFile);
        OutputPlainProperty(TXT_SYSTEMDEVICES_DEVICEKEY,
                            systemDevice->szDeviceID, hFile);

        /* Drivers */
        if (systemDevice->drivers != NULL)
            OutputDriversForPlain(hFile, systemDevice->drivers,
                                  systemDevice->numDrivers, -1);
        else
            OutputPlainProperty(TXT_DRIVERS_DRIVER, s_notApplicable, hFile);

        if (systemDeviceNum <
            g_dxdiagcom_root.numSystemDevices - 1)
            OutputNewline(hFile);
        /* END SYSTEM DEVICE */
    }

    /* DirectXComponents */
    OutputSectionHeading(TXT_DXCOMPONENTS, hFile);
    for (fileNum = 0; fileNum < directXFiles->numDirectXFiles; fileNum++) {
        /* FOR EACH FILE */
        file = directXFiles->directXFiles[fileNum];

        OutputPlainDirectXFileProperty(file, hFile);
        /* END FILE */
    }
    /* DirectShowFilters */
    OutputSectionHeading(TXT_DIRECTSHOW_FILTERS, hFile);
    /* for loop is used to jump through code, with incrementation taken care of inside */
    for (filterNum = 0;
         filterNum < g_dxdiagcom_root.numDirectShowFilters; ) {
        /* FOR EACH FILTER */
        WCHAR *currentCategory;
        DWORD numInCategory;
        DWORD tempIndex;
        DWORD shiftedMax;

        filter = directShow[filterNum];
        currentCategory = filter->szCatName;

        OutputPlainStringTitle(currentCategory, hFile);
        OutputNewline(hFile);

        /* Find number of filters in this category, then display them */
        numInCategory = 1;
        tempIndex = filterNum+1;
        while (tempIndex < g_dxdiagcom_root.numDirectShowFilters) {
            filter = directShow[tempIndex++];

            if (lstrcmpW(filter->szCatName, currentCategory) != 0)
                break;

            numInCategory++;
        }

        tempIndex = filterNum;
        shiftedMax = filterNum+numInCategory;
        while (tempIndex < shiftedMax) {
            filter = directShow[tempIndex++];
            OutputPlainDirectShowFilterProperty(filter, hFile);
        }
        OutputNewline(hFile);
        filterNum = tempIndex;

        /* END FILTER */
    }

}

static void OutputDriversForXML(FILE * hFile, DxDiag_Driver ** drivers,
                             int numDrivers)
{
    DxDiag_Driver *driver;
    int driverNum;

    OutputNodeBegin(XML_DRIVERS, hFile);
    for (driverNum = 0; driverNum < numDrivers; driverNum++) {
        driver = drivers[driverNum];

        OutputNodeBegin(XML_DRIVERS_DRIVER, hFile);
        OutputXMLProperty(XML_DRIVERS_NAME, driver->szName, hFile);
        OutputXMLProperty(XML_DRIVERS_PATH, driver->szPath, hFile);
        OutputXMLProperty(XML_DRIVERS_VERSION, driver->szVersion, hFile);
        OutputXMLProperty(XML_DRIVERS_LANGUAGE, driver->szLanguageEnglish,
                          hFile);
        OutputXMLProperty(XML_DRIVERS_BETA,
                          BoolStringToNumString(driver->bBeta), hFile);
        OutputXMLProperty(XML_DRIVERS_DEBUG,
                          BoolStringToNumString(driver->bDebug), hFile);
        OutputXMLProperty(XML_DRIVERS_DATE, driver->szDatestampEnglish,
                          hFile);
        OutputXMLProperty(XML_DRIVERS_SIZE, driver->lNumBytes, hFile);
        OutputNodeEnd(XML_DRIVERS_DRIVER, hFile);
    }
    OutputNodeEnd(XML_DRIVERS, hFile);

}

static void OutputInputRelatedDeviceForXML(FILE * hFile,
                                        DxDiag_DirectInputRelatedDevice *
                                        inputRelatedDevice)
{
    int inputRelatedDeviceNum;
    OutputNodeBegin(XML_DIRECTINPUT_INPUTRELATEDDEVICE, hFile);

    /* Properties */
    OutputXMLProperty(XML_DIRECTINPUT_DESCRIPTION,
                      inputRelatedDevice->szDescription, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_VENDORID,
                      inputRelatedDevice->dwVendorID, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_PRODUCTID,
                      inputRelatedDevice->dwProductID, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_LOCATION,
                      inputRelatedDevice->szLocation, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_MATCHINGDEVICEID,
                      inputRelatedDevice->szMatchingDeviceId, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_UPPERFILTERS,
                      inputRelatedDevice->szUpperFilters, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_LOWERFILTERS,
                      inputRelatedDevice->szLowerFilters, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_SERVICE,
                      inputRelatedDevice->szService, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_OEMDATA,
                      inputRelatedDevice->szOEMData, hFile);
    OutputXMLProperty(XML_DIRECTINPUT_FLAGS1, inputRelatedDevice->szFlags1,
                      hFile);
    OutputXMLProperty(XML_DIRECTINPUT_FLAGS2, inputRelatedDevice->szFlags2,
                      hFile);

    /* Drivers */
    if (inputRelatedDevice->drivers != NULL) {
        OutputDriversForXML(hFile, inputRelatedDevice->drivers,
                            inputRelatedDevice->numDrivers);
    }

    /* Input Related Devices */
    if (inputRelatedDevice->directInputRelatedDevices != NULL) {
        for (inputRelatedDeviceNum = 0; inputRelatedDeviceNum
             < inputRelatedDevice->numDirectInputRelatedDevices;
             inputRelatedDeviceNum++) {
            OutputInputRelatedDeviceForXML(hFile,
                                        inputRelatedDevice->
                                        directInputRelatedDevices
                                        [inputRelatedDeviceNum]);
        }

    }
    OutputNodeEnd(XML_DIRECTINPUT_INPUTRELATEDDEVICE, hFile);

}

static void OutputXMLFile(FILE * hFile)
{
    /* CONTAINERS AND ITERATORS */
    /* SystemInfo */
    DxDiag_SystemInfo *systemInfo = g_dxdiagcom_root.systemInfo;

    /* DisplayDevices */
    DxDiag_DisplayDevice **displayDevices =
        g_dxdiagcom_root.displayDevices;
    DxDiag_DisplayDevice *displayDevice = NULL;
    DWORD displayDeviceNum;

    DxDiag_DXVADeinterlaceCap **dxvaDeinterlaceCaps = NULL;
    DxDiag_DXVADeinterlaceCap *dxvaDeinterlaceCap = NULL;
    DWORD dxvaDeinterlaceCapNum;

    /* SoundDevices */
    DxDiag_SoundDevice **soundDevices = g_dxdiagcom_root.soundDevices;
    DxDiag_SoundDevice *soundDevice = NULL;
    DWORD soundDeviceNum;

    /* SoundCaptureDevices */
    DxDiag_SoundCaptureDevice **soundCaptureDevices =
        g_dxdiagcom_root.soundCaptureDevices;
    DxDiag_SoundCaptureDevice *soundCaptureDevice = NULL;
    DWORD soundCaptureDeviceNum;

    /* DirectMusic */
    DxDiag_DirectMusic *directMusic = g_dxdiagcom_root.directMusic;
    DxDiag_DirectMusicPort **ports = directMusic->directMusicPorts;
    DxDiag_DirectMusicPort *port = NULL;
    DWORD portNum;

    /* DirectInput */
    DxDiag_DirectInput *directInput = g_dxdiagcom_root.directInput;

    DxDiag_DirectInputDevice **directInputDevices =
        directInput->directInputDevices;
    DxDiag_DirectInputDevice *directInputDevice = NULL;
    DWORD directInputDeviceNum;

    DxDiag_DirectInputRelatedDevice **usbRoot =
        directInput->directInputUSBs;
    DxDiag_DirectInputRelatedDevice *usbDevice = NULL;
    DWORD usbDeviceNum;

    DxDiag_DirectInputRelatedDevice **gameportDevices =
        directInput->directInputGamePorts;
    DxDiag_DirectInputRelatedDevice *gameportDevice = NULL;
    DWORD gameportDeviceNum;

    DxDiag_DirectInputRelatedDevice **ps2Devices =
        directInput->directInputPs2Devices;
    DxDiag_DirectInputRelatedDevice *ps2Device = NULL;
    DWORD ps2DeviceNum;

    /* DirectPlay */
    DxDiag_DirectPlay *directPlay = g_dxdiagcom_root.directPlay;

    DxDiag_DirectPlaySp **directPlaySPs = directPlay->directPlaySps;
    DxDiag_DirectPlaySp *directPlaySP = NULL;
    DWORD directPlaySPNum;

    DxDiag_DirectPlayAdapter **directPlayAdapters =
        directPlay->directPlayAdapters;
    DxDiag_DirectPlayAdapter *directPlayAdapter = NULL;
    DWORD directPlayAdapterNum;

    /* WINE_FIXME: Add LobbyableApps and VoiceCodecs once code done */

    /* LogicalDisks */
    DxDiag_LogicalDisk **logicalDisks = g_dxdiagcom_root.logicalDisks;
    DxDiag_LogicalDisk *logicalDisk = NULL;
    DWORD logicalDiskNum;

    /* SystemDevices */
    DxDiag_SystemDevice **systemDevices = g_dxdiagcom_root.systemDevices;
    DxDiag_SystemDevice *systemDevice = NULL;
    DWORD systemDeviceNum;

    /* DirectXFiles */
    DxDiag_DirectXFiles *directXFiles = g_dxdiagcom_root.directXFiles;
    DxDiag_DirectXFile *file = NULL;
    DWORD fileNum;

    /* DirectShow */
    DxDiag_DirectShowFilter **directShow =
        g_dxdiagcom_root.directShowFilters;
    DxDiag_DirectShowFilter *filter = NULL;
    DWORD filterNum;

    /* STRINGS */
    /* DirectMusic */
    static WCHAR s_xmlDeclaration[] =
        { '<', '?', 'x', 'm', 'l', ' ', 'v', 'e', 'r', 's', 'i', 'o', 'n',
        '=', '\'', '1', '.', '0', '\'', '?', '>', 0
    };
    static WCHAR s_notApplicable[] = { 'n', '/', 'a', 0 };
    WCHAR boolFalse[MAX_STRING_LEN];

    /* STRING INITIALIZATION */
    LoadStringW(g_hinstance, STRING_COMMON_FALSE, boolFalse,
                MAX_STRING_LEN);

    /* XML FILE POPULATION */
    /* Declaration and Root */
    OutputWString(hFile, s_xmlDeclaration);
    OutputNodeBegin(XML_ROOT, hFile);

    /* SystemInfo */
    OutputNodeBegin(XML_SYSTEM, hFile);
    OutputXMLProperty(XML_SYSTEM_TIME, systemInfo->szTimeEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_MACHINENAME,
                      systemInfo->szMachineNameLocalized, hFile);
    OutputXMLProperty(XML_SYSTEM_OS, systemInfo->szOSExLongEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_LANGUAGE,
                      systemInfo->szLanguagesEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_MANUFACTURER,
                      systemInfo->szSystemManufacturerEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_MODEL,
                      systemInfo->szSystemModelEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_BIOS, systemInfo->szBIOSEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_PROCESSOR,
                      systemInfo->szProcessorEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_MEMORY,
                      systemInfo->szPhysicalMemoryEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_PAGEFILE,
                      systemInfo->szPageFileLocalized, hFile);
    OutputXMLProperty(XML_SYSTEM_WINDIR, systemInfo->szWindowsDir, hFile);
    OutputXMLProperty(XML_SYSTEM_DXVERSION,
                      systemInfo->szDirectXVersionLongEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_DXSETUPPARAMS,
                      systemInfo->szSetupParamEnglish, hFile);
    OutputXMLProperty(XML_SYSTEM_DXDIAGVER,
                      systemInfo->szDxDiagVersion, hFile);
    OutputXMLProperty(XML_SYSTEM_DXDIAGUNICODE,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputXMLProperty(XML_SYSTEM_DXDIAG64BIT,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_SYSTEM, hFile);

    /* DxDiagNotes */
    OutputNodeBegin(XML_DXDIAGNOTES, hFile);

    OutputXMLProperty(XML_DXDIAGNOTES_DXFILESTAB,
                      TakeBeforeNewline
                      (directXFiles->szDXFileNotesEnglish), hFile);

    for (displayDeviceNum = 0; displayDeviceNum
         < g_dxdiagcom_root.numDisplayDevices; displayDeviceNum++) {
        displayDevice = displayDevices[displayDeviceNum];
        OutputXMLProperty(XML_DXDIAGNOTES_DISPLAYTAB,
                          TakeBeforeNewline(displayDevice->szNotesEnglish),
                          hFile);
    }

    for (soundDeviceNum = 0; soundDeviceNum
         < g_dxdiagcom_root.numSoundDevices; soundDeviceNum++) {
        soundDevice = soundDevices[soundDeviceNum];
        OutputXMLProperty(XML_DXDIAGNOTES_SOUNDTAB,
                          TakeBeforeNewline(soundDevice->szNotesEnglish),
                          hFile);
    }

    OutputXMLProperty(XML_DXDIAGNOTES_MUSICTAB,
                      TakeBeforeNewline(directMusic->szNotesEnglish),
                      hFile);
    OutputXMLProperty(XML_DXDIAGNOTES_INPUTTAB,
                      TakeBeforeNewline(directInput->szInputNotesEnglish),
                      hFile);
    OutputXMLProperty(XML_DXDIAGNOTES_NETWORKTAB,
                      TakeBeforeNewline(directPlay->szNetworkNotesEnglish),
                      hFile);

    OutputNodeEnd(XML_DXDIAGNOTES, hFile);

    /* DxDiagDebugLevels */
    OutputNodeBegin(XML_DXDEBUGLEVELS, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECT3D, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nD3DDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECT3D, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTDRAW, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDDrawDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTDRAW, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTINPUT, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDIDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTINPUT, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTMUSIC, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDMusicDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTMUSIC, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTPLAY, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDPlayDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTPLAY, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTSOUND, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDSoundDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTSOUND, hFile);

    OutputNodeBegin(XML_DXDEBUGLEVELS_DIRECTSHOW, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_CURRENT,
                      systemInfo->nDShowDebugLevel, hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_MAX, LSTRING_COMMON_WINE_FIXME,
                      hFile);
    OutputXMLProperty(XML_DXDEBUGLEVELS_RUNTIME,
                      LSTRING_COMMON_WINE_FIXME, hFile);
    OutputNodeEnd(XML_DXDEBUGLEVELS_DIRECTSHOW, hFile);

    OutputNodeEnd(XML_DXDEBUGLEVELS, hFile);

    /* DisplayDevices */
    OutputNodeBegin(XML_DISPLAYDEVICES, hFile);
    for (displayDeviceNum = 0; displayDeviceNum
         < g_dxdiagcom_root.numDisplayDevices; displayDeviceNum++) {
        /* FOR EACH DISPLAY DEVICE */
        displayDevice = displayDevices[displayDeviceNum];

        OutputNodeBegin(XML_DISPLAYDEVICES_DISPLAYDEVICE, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_CARDNAME,
                          displayDevice->szDescription, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MANUFACTURER,
                          displayDevice->szManufacturer, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_CHIPTYPE,
                          displayDevice->szChipType, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DACTYPE,
                          displayDevice->szDACType, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DEVICEKEY,
                          displayDevice->szKeyDeviceID, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DISPLAYMEMORY,
                          displayDevice->szDisplayMemoryEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_CURRENTMODE,
                          displayDevice->szDisplayModeEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MONITOR,
                          displayDevice->szMonitorName, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MONITORMAXRES,
                          displayDevice->szMonitorMaxRes, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERNAME,
                          displayDevice->szDriverName, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERVERSION,
                          displayDevice->szDriverVersion, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERLANGUAGE,
                          displayDevice->szDriverLanguageEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DDIVERSION,
                          displayDevice->szDDIVersionEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERBETA,
                          BoolStringToNumString
                          (displayDevice->bDriverBeta), hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERDEBUG,
                          BoolStringToNumString
                          (displayDevice->bDriverDebug), hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERDATE,
                          displayDevice->szDriverDateEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERSIZE,
                          displayDevice->lDriverSize, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DRIVERWHQLLOGO,
                          BoolStringToReadableString
                          (displayDevice->bDriverSigned), hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_WHQLDATESTAMP,
                          StringExistenceCheck
                          (displayDevice->szDriverSignDate), hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_VDD, displayDevice->szVdd,
                          hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MINIVDD,
                          displayDevice->szMiniVdd, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MINIVDDDATE,
                          displayDevice->szMiniVddDateEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_MINIVDDSIZE,
                          displayDevice->lMiniVddSize, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DEVICEIDENTIFIER,
                          displayDevice->szDeviceIdentifier, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_VENDORID,
                          displayDevice->szVendorId, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DEVICEID,
                          displayDevice->szDeviceId, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_SUBSYSID,
                          displayDevice->szSubSysId, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_REVISIONID,
                          displayDevice->szRevisionId, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DXVAMODES,
                          displayDevice->szDXVAModes, hFile);

        /* DXVA DEINTERLACE CAPS */
        OutputNodeBegin(XML_DISPLAYDEVICES_DXVADEINTERLACECAPS, hFile);
        dxvaDeinterlaceCaps = displayDevice->dxvaDeinterlaceCaps;
        for (dxvaDeinterlaceCapNum = 0; dxvaDeinterlaceCapNum
             < displayDevice->numDxvaDeinterlaceCaps;
             dxvaDeinterlaceCapNum++) {
            /* FOR EACH DEINTERLACE CAP */
            dxvaDeinterlaceCap =
                dxvaDeinterlaceCaps[dxvaDeinterlaceCapNum];

            OutputNodeBegin(XML_DISPLAYDEVICES_DXVADEINTERLACECAP, hFile);
            OutputXMLProperty(XML_DISPLAYDEVICES_DXVA_GUID,
                              dxvaDeinterlaceCap->szGuid, hFile);
            OutputXMLProperty(XML_DISPLAYDEVICES_DXVA_D3DINPUTFORMAT,
                              dxvaDeinterlaceCap->szD3DInputFormat, hFile);
            OutputXMLProperty(XML_DISPLAYDEVICES_DXVA_D3DOUTPUTFORMAT,
                              dxvaDeinterlaceCap->szD3DOutputFormat,
                              hFile);
            OutputXMLProperty(XML_DISPLAYDEVICES_DXVA_CAPS,
                              dxvaDeinterlaceCap->szCaps, hFile);
            OutputXMLProperty
                (XML_DISPLAYDEVICES_DXVA_NUMPREVOUTPUTFRAMES,
                 dxvaDeinterlaceCap->dwNumPreviousOutputFrames, hFile);
            OutputXMLProperty
                (XML_DISPLAYDEVICES_DXVA_NUMFORWARDREFSAMPLES,
                 dxvaDeinterlaceCap->dwNumForwardRefSamples, hFile);
            OutputXMLProperty
                (XML_DISPLAYDEVICES_DXVA_NUMBACKWARDREFSAMPLES,
                 dxvaDeinterlaceCap->dwNumBackwardRefSamples, hFile);
            OutputNodeEnd(XML_DISPLAYDEVICES_DXVADEINTERLACECAP, hFile);
        }
        OutputNodeEnd(XML_DISPLAYDEVICES_DXVADEINTERLACECAPS, hFile);

        OutputXMLProperty(XML_DISPLAYDEVICES_REGISTRY,
                          PreprocessRegistryStringForXML
                          (displayDevice->szRegHelpText), hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DDRAWSTATUS,
                          displayDevice->szDDStatusEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_D3DSTATUS,
                          displayDevice->szD3DStatusEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_AGPSTATUS,
                          displayDevice->szAGPStatusEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_DDRAWTESTRESULT,
                          displayDevice->szTestResultDDEnglish, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_D3D7TESTRESULT,
                          displayDevice->szTestResultD3D7English, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_D3D8TESTRESULT,
                          displayDevice->szTestResultD3D8English, hFile);
        OutputXMLProperty(XML_DISPLAYDEVICES_D3D9TESTRESULT,
                          displayDevice->szTestResultD3D9English, hFile);

        OutputNodeEnd(XML_DISPLAYDEVICES_DISPLAYDEVICE, hFile);
        /* END DISPLAY DEVICE */
    }
    OutputNodeEnd(XML_DISPLAYDEVICES, hFile);

    /* DirectSound */
    OutputNodeBegin(XML_DIRECTSOUND, hFile);

    /* SoundDevices */
    OutputNodeBegin(XML_DIRECTSOUND_SOUNDDEVICES, hFile);
    for (soundDeviceNum = 0;
         soundDeviceNum < g_dxdiagcom_root.numSoundDevices;
         soundDeviceNum++) {
        /* FOR EACH SOUND DEVICE */
        soundDevice = soundDevices[soundDeviceNum];
        if (!soundDevice)
            continue;

        OutputNodeBegin(XML_DIRECTSOUND_SOUNDDEVICE, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DESCRIPTION,
                          soundDevice->szDescription, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_HARDWAREID,
                          soundDevice->szHardwareID, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MANUFACTURERID,
                          soundDevice->szManufacturerID, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_PRODUCTID,
                          soundDevice->szProductID, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_TYPE, soundDevice->szType,
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERNAME,
                          soundDevice->szDriverName, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERVERSION,
                          soundDevice->szDriverVersion, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERLANGUAGE,
                          soundDevice->szDriverLanguageLocalized, hFile);

        OutputXMLProperty(XML_DIRECTSOUND_DRIVERBETA,
                          BoolStringToNumString(soundDevice->bDriverBeta),
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERDEBUG,
                          BoolStringToNumString(soundDevice->bDriverDebug),
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERWHQLLOGO,
                          BoolStringToNumString
                          (soundDevice->bDriverSigned), hFile);

        OutputXMLProperty(XML_DIRECTSOUND_DRIVERDATE,
                          soundDevice->szDriverDateLocalized, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERSIZE,
                          soundDevice->lNumBytes, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_OTHERFILES,
                          soundDevice->szOtherDrivers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERPROVIDER,
                          soundDevice->szProvider, hFile);

        OutputXMLProperty(XML_DIRECTSOUND_HWACCELLEVEL,
                          NumStringToReadableString
                          (soundDevice->lAccelerationLevel), hFile);

        OutputXMLProperty(XML_DIRECTSOUND_DEFAULTSOUNDPLAYBACK,
                          BoolStringToNumString
                          (soundDevice->bDefaultSoundPlayback), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DEFAULTVOICEPLAYBACK,
                          BoolStringToNumString
                          (soundDevice->bDefaultVoicePlayback), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_VOICEMANAGER,
                          BoolStringToNumString
                          (soundDevice->bVoiceManager), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_EAX20LISTENER,
                          BoolStringToNumString
                          (soundDevice->bEAX20Listener), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_EAX20SOURCE,
                          BoolStringToNumString(soundDevice->bEAX20Source),
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_I3DL2LISTENER,
                          BoolStringToNumString
                          (soundDevice->bI3DL2Listener), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_I3DL2SOURCE,
                          BoolStringToNumString
                          (soundDevice->bI3DL2Listener), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_ZOOMFX,
                          BoolStringToNumString(soundDevice->bZoomFX),
                          hFile);

        OutputXMLProperty(XML_DIRECTSOUND_FLAGS, soundDevice->dwFlags,
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MINSECONDARYSAMPLERATE,
                          soundDevice->dwMinSecondarySampleRate, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXSECONDARYSAMPLERATE,
                          soundDevice->dwMaxSecondarySampleRate, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_PRIMARYBUFFERS,
                          soundDevice->dwPrimaryBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHWMIXING_ALLBUFFERS,
                          soundDevice->dwMaxHwMixingAllBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHWMIXING_STATICBUFFERS,
                          soundDevice->dwMaxHwMixingStaticBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHWMIXING_STREAMINGBUFFERS,
                          soundDevice->dwMaxHwMixingStreamingBuffers,
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHWMIXING_ALLBUFFERS,
                          soundDevice->dwFreeHwMixingAllBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHWMIXING_STATICBUFFERS,
                          soundDevice->dwFreeHwMixingStaticBuffers, hFile);
        OutputXMLProperty
            (XML_DIRECTSOUND_FREEHWMIXING_STREAMINGBUFFERS,
             soundDevice->dwFreeHwMixingStreamingBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHW3D_ALLBUFFERS,
                          soundDevice->dwMaxHw3DAllBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHW3D_STATICBUFFERS,
                          soundDevice->dwMaxHw3DStaticBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXHW3D_STREAMINGBUFFERS,
                          soundDevice->dwMaxHw3DStreamingBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHW3D_ALLBUFFERS,
                          soundDevice->dwFreeHw3DAllBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHW3D_STATICBUFFERS,
                          soundDevice->dwFreeHw3DStaticBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHW3D_STREAMINGBUFFERS,
                          soundDevice->dwFreeHw3DStreamingBuffers, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_TOTALHWMEMBYTES,
                          soundDevice->dwTotalHwMemBytes, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FREEHWMEMBYTES,
                          soundDevice->dwFreeHwMemBytes, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_MAXCONTIGFREEHWMEMBYTES,
                          soundDevice->dwMaxContigFreeHwMemBytes, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_UNLOCKTRANSFERRATEHWBUFFERS,
                          soundDevice->dwUnlockTransferRateHwBuffers,
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_PLAYCPUOVERHEADSWBUFFERS,
                          soundDevice->dwPlayCpuOverheadSwBuffers, hFile);

        OutputXMLProperty(XML_DIRECTSOUND_REGISTRY,
                          PreprocessRegistryStringForXML
                          (soundDevice->szRegHelpText), hFile);
        OutputNodeEnd(XML_DIRECTSOUND_SOUNDDEVICE, hFile);
        /* END SOUND DEVICE */
    }
    OutputNodeEnd(XML_DIRECTSOUND_SOUNDDEVICES, hFile);

    /* SoundCaptureDevices */
    OutputNodeBegin(XML_DIRECTSOUND_SOUNDCAPTUREDEVICES, hFile);
    for (soundCaptureDeviceNum = 0; soundCaptureDeviceNum
         < g_dxdiagcom_root.numSoundCaptureDevices;
         soundCaptureDeviceNum++) {
        /* FOR EACH SOUNDCAPTURE DEVICE */
        soundCaptureDevice = soundCaptureDevices[soundCaptureDeviceNum];

        OutputNodeBegin(XML_DIRECTSOUND_SOUNDCAPTUREDEVICE, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DESCRIPTION,
                          soundCaptureDevice->szDescription, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERNAME,
                          soundCaptureDevice->szDriverName, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERVERSION,
                          soundCaptureDevice->szDriverVersion, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERLANGUAGE,
                          soundCaptureDevice->szDriverLanguageEnglish,
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERBETA,
                          BoolStringToNumString
                          (soundCaptureDevice->bDriverBeta), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERDEBUG,
                          BoolStringToNumString
                          (soundCaptureDevice->bDriverDebug), hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERDATE,
                          soundCaptureDevice->szDriverDateEnglish, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DRIVERSIZE,
                          soundCaptureDevice->lNumBytes, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DEFAULTSOUNDRECORDING,
                          BoolStringToNumString
                          (soundCaptureDevice->bDefaultSoundRecording),
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_DEFAULTVOICERECORDING,
                          BoolStringToNumString
                          (soundCaptureDevice->bDefaultVoiceRecording),
                          hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FLAGS,
                          soundCaptureDevice->dwFlags, hFile);
        OutputXMLProperty(XML_DIRECTSOUND_FORMATS,
                          soundCaptureDevice->dwFormats, hFile);
        OutputNodeEnd(XML_DIRECTSOUND_SOUNDCAPTUREDEVICE, hFile);
        /* END SOUNDCAPTURE DEVICE */
    }
    OutputNodeEnd(XML_DIRECTSOUND_SOUNDCAPTUREDEVICES, hFile);

    OutputNodeEnd(XML_DIRECTSOUND, hFile);

    /* DirectMusic */
    OutputNodeBegin(XML_DIRECTMUSIC, hFile);
    OutputXMLProperty(XML_DIRECTMUSIC_DLSPATH,
                      directMusic->szGMFilePath, hFile);
    OutputXMLProperty(XML_DIRECTMUSIC_DLSVERSION,
                      directMusic->szGMFileVersion, hFile);
    if (lstrcmpW(directMusic->bAccelerationExists, boolFalse) == 0)
        OutputXMLProperty(XML_DIRECTMUSIC_ACCELERATION,
                          s_notApplicable, hFile);
    else
        OutputXMLProperty(XML_DIRECTMUSIC_ACCELERATION,
                          BoolStringToReadableString
                          (directMusic->bAccelerationEnabled), hFile);

    OutputNodeBegin(XML_DIRECTMUSIC_PORTS, hFile);
    for (portNum = 0; portNum < directMusic->numDirectMusicPorts;
         portNum++) {
        /* FOR EACH PORT */
        port = ports[portNum];

        OutputNodeBegin(XML_DIRECTMUSIC_PORT, hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_DESCRIPTION,
                          port->szDescription, hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_SOFTWARE,
                          BoolStringToNumString(port->bSoftware), hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_KERNELMODE,
                          BoolStringToNumString(port->bKernelMode), hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_OUTPUTPORT,
                          BoolStringToNumString(port->bOutputPort), hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_USESDLS,
                          BoolStringToNumString(port->bUsesDLS), hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_EXTERNAL,
                          BoolStringToNumString(port->bExternal), hFile);
        OutputXMLProperty(XML_DIRECTMUSIC_DEFAULTPORT,
                          BoolStringToNumString(port->bDefaultPort),
                          hFile);
        OutputNodeEnd(XML_DIRECTMUSIC_PORT, hFile);
        /* END PORTS */
    }
    OutputNodeEnd(XML_DIRECTMUSIC_PORTS, hFile);

    OutputXMLProperty(XML_DIRECTMUSIC_REGISTRY,
                      directMusic->szRegHelpText, hFile);
    OutputNodeEnd(XML_DIRECTMUSIC, hFile);

    /* DirectInput */
    OutputNodeBegin(XML_DIRECTINPUT, hFile);

    /* DirectInputDevices */
    OutputNodeBegin(XML_DIRECTINPUT_DEVICES, hFile);
    for (directInputDeviceNum = 0; directInputDeviceNum
         < directInput->numDirectInputDevices; directInputDeviceNum++) {
        /* FOR EACH DIRECTINPUT DEVICE */
        directInputDevice = directInputDevices[directInputDeviceNum];

        OutputNodeBegin(XML_DIRECTINPUT_DEVICE, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_DEVICENAME,
                          directInputDevice->szInstanceName, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_ATTACHED,
                          BoolStringToNumString
                          (directInputDevice->bAttached), hFile);
        OutputXMLProperty(XML_DIRECTINPUT_JOYSTICKID,
                          directInputDevice->dwJoystickID, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_VENDORID,
                          directInputDevice->dwVendorID, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_PRODUCTID,
                          directInputDevice->dwProductID, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_DEVTYPE,
                          directInputDevice->dwDevType, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_FFDRIVERNAME,
                          directInputDevice->szFFDriverName, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_FFDRIVERDATEENGLISH,
                          directInputDevice->szFFDriverDateEnglish, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_FFDRIVERVERSION,
                          directInputDevice->szFFDriverVersion, hFile);
        OutputXMLProperty(XML_DIRECTINPUT_FFDRIVERSIZE,
                          directInputDevice->lFFDriverSize, hFile);
        OutputNodeEnd(XML_DIRECTINPUT_DEVICE, hFile);
        /* END DIRECTINPUT DEVICES */
    }
    OutputNodeEnd(XML_DIRECTINPUT_DEVICES, hFile);

    /* DirectInputUSBDevices */
    OutputNodeBegin(XML_DIRECTINPUT_USBROOT, hFile);
    for (usbDeviceNum = 0; usbDeviceNum
         < directInput->numDirectInputUSBs; usbDeviceNum++) {
        usbDevice = usbRoot[usbDeviceNum];
        OutputInputRelatedDeviceForXML(hFile, usbDevice);
    }
    OutputNodeEnd(XML_DIRECTINPUT_USBROOT, hFile);

    /* DirectInputGameportDevices */
    OutputNodeBegin(XML_DIRECTINPUT_GAMEPORTDEVICES, hFile);
    for (gameportDeviceNum = 0; gameportDeviceNum
         < directInput->numDirectInputGamePorts; gameportDeviceNum++) {
        gameportDevice = gameportDevices[gameportDeviceNum];
        OutputInputRelatedDeviceForXML(hFile, gameportDevice);
    }
    OutputNodeEnd(XML_DIRECTINPUT_GAMEPORTDEVICES, hFile);

    /* DirectInputPS2Devices */
    OutputNodeBegin(XML_DIRECTINPUT_PS2DEVICES, hFile);
    for (ps2DeviceNum = 0; ps2DeviceNum
         < directInput->numDirectInputPs2Devices; ps2DeviceNum++) {
        ps2Device = ps2Devices[ps2DeviceNum];
        OutputInputRelatedDeviceForXML(hFile, ps2Device);
    }
    OutputNodeEnd(XML_DIRECTINPUT_PS2DEVICES, hFile);

    OutputXMLProperty(XML_DIRECTINPUT_POLLWITHINTERRUPT,
                      BoolStringToReadableString(directInput->bPollFlags),
                      hFile);
    OutputXMLProperty(XML_DIRECTINPUT_REGISTRY,
                      StringExistenceCheck(directInput->szRegHelpText),
                      hFile);
    OutputNodeEnd(XML_DIRECTINPUT, hFile);

    /* DirectPlay */
    OutputNodeBegin(XML_DIRECTPLAY, hFile);

    /* DirectPlayServiceProviders */
    OutputNodeBegin(XML_DIRECTPLAY_SERVICEPROVIDERS, hFile);
    for (directPlaySPNum = 0; directPlaySPNum
         < directPlay->numDirectPlaySps; directPlaySPNum++) {
        /* FOR EACH SERVICE PROVIDER */
        directPlaySP = directPlaySPs[directPlaySPNum];

        OutputNodeBegin(XML_DIRECTPLAY_SERVICEPROVIDER, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_NAME,
                          directPlaySP->szNameEnglish, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_REGISTRY,
                          BoolStringToNumString(directPlaySP->bRegistryOK),
                          hFile);
        OutputXMLProperty(XML_DIRECTPLAY_FILE, directPlaySP->szFile,
                          hFile);
        OutputXMLProperty(XML_DIRECTPLAY_VERSION,
                          directPlaySP->szVersionEnglish, hFile);
        OutputNodeEnd(XML_DIRECTPLAY_SERVICEPROVIDER, hFile);
        /* END SERVICE PROVIDERS */
    }
    OutputNodeEnd(XML_DIRECTPLAY_SERVICEPROVIDERS, hFile);

    /* DirectPlayLobbyableApps */
    /* WINE_FIXME: NOT IMPLEMENTED */
    OutputNodeBegin(XML_DIRECTPLAY_LOBBYABLEAPPS, hFile);
    OutputNodeBegin(XML_DIRECTPLAY_LOBBYABLEAPP, hFile);
    OutputNodeEnd(XML_DIRECTPLAY_LOBBYABLEAPP, hFile);
    OutputNodeEnd(XML_DIRECTPLAY_LOBBYABLEAPPS, hFile);

    /* DirectPlayAdapters */
    OutputNodeBegin(XML_DIRECTPLAY_ADAPTERS, hFile);
    for (directPlayAdapterNum = 0; directPlayAdapterNum
         < directPlay->numDirectPlayAdapters; directPlayAdapterNum++) {
        /* FOR EACH ADAPTER */
        directPlayAdapter = directPlayAdapters[directPlayAdapterNum];

        OutputNodeBegin(XML_DIRECTPLAY_ADAPTER, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_NAME,
                          directPlayAdapter->szAdapterName, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_SPNAME,
                          directPlayAdapter->szSPNameEnglish, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_GUID,
                          directPlayAdapter->szGuid, hFile);
        OutputXMLProperty(XML_DIRECTPLAY_FLAGS,
                          directPlayAdapter->dwFlags, hFile);
        OutputNodeEnd(XML_DIRECTPLAY_ADAPTER, hFile);
        /* END ADAPTERS */
    }
    OutputNodeEnd(XML_DIRECTPLAY_ADAPTERS, hFile);

    /* DirectPlayVoiceCodecs */
    /* WINE_FIXME: NOT IMPLEMENTED */
    OutputNodeBegin(XML_DIRECTPLAY_VOICECODECS, hFile);
    OutputNodeBegin(XML_DIRECTPLAY_VOICECODEC, hFile);
    OutputNodeEnd(XML_DIRECTPLAY_VOICECODEC, hFile);
    OutputNodeEnd(XML_DIRECTPLAY_VOICECODECS, hFile);

    OutputXMLProperty(XML_DIRECTPLAY_REGISTRY,
                      StringExistenceCheck(directPlay->szRegHelpText),
                      hFile);
    OutputXMLProperty(XML_DIRECTPLAY_TESTRESULTS,
                      directPlay->szTestResultEnglish, hFile);
    OutputXMLProperty(XML_DIRECTPLAY_VOICEWIZARD_FULLDUPLEXTEST,
                      directPlay->szVoiceWizardFullDuplexTestEnglish,
                      hFile);
    OutputXMLProperty(XML_DIRECTPLAY_VOICEWIZARD_HALFDUPLEXTEST,
                      directPlay->szVoiceWizardHalfDuplexTestEnglish,
                      hFile);
    OutputXMLProperty(XML_DIRECTPLAY_VOICEWIZARD_MICTEST,
                      directPlay->szVoiceWizardMicTestEnglish, hFile);
    OutputNodeEnd(XML_DIRECTPLAY, hFile);

    /* LogicalDisks */
    OutputNodeBegin(XML_LOGICALDISKS, hFile);
    for (logicalDiskNum = 0;
         logicalDiskNum < g_dxdiagcom_root.numLogicalDisks;
         logicalDiskNum++) {
        /* FOR EACH LOGICAL DISK */
        logicalDisk = logicalDisks[logicalDiskNum];

        OutputNodeBegin(XML_LOGICALDISKS_LOGICALDISK, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_DRIVELETTER,
                          logicalDisk->szDriveLetter, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_FREESPACE,
                          logicalDisk->szFreeSpace, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_MAXSPACE,
                          logicalDisk->szMaxSpace, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_FILESYSTEM,
                          logicalDisk->szFileSystem, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_MODEL, logicalDisk->szModel,
                          hFile);
        OutputXMLProperty(XML_LOGICALDISKS_PNPDEVICEID,
                          logicalDisk->szPNPDeviceID, hFile);
        OutputXMLProperty(XML_LOGICALDISKS_HARDDRIVEINDEX,
                          logicalDisk->dwHardDriveIndex, hFile);

        /* Drivers */
        if (logicalDisk->drivers != NULL)
            OutputDriversForXML(hFile, logicalDisk->drivers,
                             logicalDisk->numDrivers);

        OutputNodeEnd(XML_LOGICALDISKS_LOGICALDISK, hFile);
        /* END LOGICAL DISK */
    }
    OutputNodeEnd(XML_LOGICALDISKS, hFile);

    /* SystemDevices */
    OutputNodeBegin(XML_SYSTEMDEVICES, hFile);
    for (systemDeviceNum = 0; systemDeviceNum
         < g_dxdiagcom_root.numSystemDevices; systemDeviceNum++) {
        /* FOR EACH SYSTEM DEVICE */
        systemDevice = systemDevices[systemDeviceNum];

        OutputNodeBegin(XML_SYSTEMDEVICES_SYSTEMDEVICE, hFile);
        OutputXMLProperty(XML_SYSTEMDEVICES_NAME,
                          systemDevice->szDescription, hFile);
        OutputXMLProperty(XML_SYSTEMDEVICES_DEVICEKEY,
                          systemDevice->szDeviceID, hFile);

        /* Drivers */
        if (systemDevice->drivers != NULL)
            OutputDriversForXML(hFile, systemDevice->drivers,
                             systemDevice->numDrivers);

        OutputNodeEnd(XML_SYSTEMDEVICES_SYSTEMDEVICE, hFile);
        /* END SYSTEM DEVICE */
    }
    OutputNodeEnd(XML_SYSTEMDEVICES, hFile);

    /* DirectXComponents */
    OutputNodeBegin(XML_DXCOMPONENTS, hFile);
    for (fileNum = 0; fileNum < directXFiles->numDirectXFiles; fileNum++) {
        /* FOR EACH FILE */
        file = directXFiles->directXFiles[fileNum];

        OutputNodeBegin(XML_DXCOMPONENTS_FILE, hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_NAME, file->szName, hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_VERSION, file->szVersion,
                          hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_LANGUAGE,
                          file->szLanguageEnglish, hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_BETA,
                          BoolStringToNumString(file->bBeta), hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_DEBUG,
                          BoolStringToNumString(file->bDebug), hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_DATE,
                          file->szDatestampEnglish, hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_SIZE, file->lNumBytes, hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_EXISTS,
                          BoolStringToNumString(file->bExists), hFile);
        OutputXMLProperty(XML_DXCOMPONENTS_PROBLEM,
                          BoolStringToNumString(file->bProblem), hFile);
        OutputNodeEnd(XML_DXCOMPONENTS_FILE, hFile);
        /* END FILE */
    }
    OutputNodeEnd(XML_DXCOMPONENTS, hFile);

    /* DirectShowFilters */
    OutputNodeBegin(XML_DIRECTSHOW, hFile);
    for (filterNum = 0;
         filterNum < g_dxdiagcom_root.numDirectShowFilters; filterNum++) {
        /* FOR EACH FILTER */
        filter = directShow[filterNum];

        OutputNodeBegin(XML_DIRECTSHOW_FILTER, hFile);
        OutputXMLProperty(XML_DIRECTSHOW_NAME, filter->szName, hFile);
        OutputXMLProperty(XML_DIRECTSHOW_FILTERCATAGORY,
                          filter->szCatName, hFile);
        OutputXMLProperty(XML_DIRECTSHOW_MERIT, filter->dwMerit, hFile);
        OutputXMLProperty(XML_DIRECTSHOW_INPUTS, filter->dwInputs, hFile);
        OutputXMLProperty(XML_DIRECTSHOW_OUTPUTS, filter->dwOutputs,
                          hFile);
        OutputXMLProperty(XML_DIRECTSHOW_FILE,
                          RemoveFilePath(filter->szFileName), hFile);
        OutputXMLProperty(XML_DIRECTSHOW_FILEVERSION,
                          filter->szFileVersion, hFile);
        OutputNodeEnd(XML_DIRECTSHOW_FILTER, hFile);
        /* END FILTERS */
    }
    OutputNodeEnd(XML_DIRECTSHOW, hFile);

    OutputNodeEnd(XML_ROOT, hFile);

}
